| ... | ... | @@ -41,6 +41,8 @@ fn cmdObjCopy( |
| 41 | 41 | var compress_debug_sections: bool = false; |
| 42 | 42 | var listen = false; |
| 43 | 43 | var add_section: ?AddSectionOptions = null; |
| 44 | var set_section_alignment: ?SectionAlignmentOptions = null; |
| 45 | var set_section_flags: ?SectionFlagsOptions = null; |
| 44 | 46 | while (i < args.len) : (i += 1) { |
| 45 | 47 | const arg = args[i]; |
| 46 | 48 | if (!mem.startsWith(u8, arg, "-")) { |
| ... | ... | @@ -105,12 +107,34 @@ fn cmdObjCopy( |
| 105 | 107 | i += 1; |
| 106 | 108 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); |
| 107 | 109 | opt_extract = args[i]; |
| 110 | } else if (mem.eql(u8, arg, "--set-section-alignment")) { |
| 111 | i += 1; |
| 112 | if (i >= args.len) fatal("expected section name and alignment arguments after '{s}'", .{arg}); |
| 113 | |
| 114 | if (splitOption(args[i])) |split| { |
| 115 | const alignment = std.fmt.parseInt(u32, split.second, 10) catch |err| { |
| 116 | fatal("unable to parse alignment number: '{s}': {s}", .{ split.second, @errorName(err) }); |
| 117 | }; |
| 118 | if (!std.math.isPowerOfTwo(alignment)) fatal("alignment must be a power of two", .{}); |
| 119 | set_section_alignment = .{ .section_name = split.first, .alignment = alignment }; |
| 120 | } else { |
| 121 | fatal("unrecognized argument: '{s}', expecting <name>=<alignment>", .{args[i]}); |
| 122 | } |
| 123 | } else if (mem.eql(u8, arg, "--set-section-flags")) { |
| 124 | i += 1; |
| 125 | if (i >= args.len) fatal("expected section name and filename arguments after '{s}'", .{arg}); |
| 126 | |
| 127 | if (splitOption(args[i])) |split| { |
| 128 | set_section_flags = .{ .section_name = split.first, .flags = split.second }; |
| 129 | } else { |
| 130 | fatal("unrecognized argument: '{s}', expecting <name>=<flags>", .{args[i]}); |
| 131 | } |
| 108 | 132 | } else if (mem.eql(u8, arg, "--add-section")) { |
| 109 | 133 | i += 1; |
| 110 | | if (i >= args.len) fatal("expected name and filename arguments after '{s}'", .{arg}); |
| 134 | if (i >= args.len) fatal("expected section name and filename arguments after '{s}'", .{arg}); |
| 111 | 135 | |
| 112 | 136 | if (splitOption(args[i])) |split| { |
| 113 | | add_section = .{ .section_name = split.first, .file = split.second }; |
| 137 | add_section = .{ .section_name = split.first, .file_path = split.second }; |
| 114 | 138 | } else { |
| 115 | 139 | fatal("unrecognized argument: '{s}', expecting <name>=<file>", .{args[i]}); |
| 116 | 140 | } |
| ... | ... | @@ -167,6 +191,8 @@ fn cmdObjCopy( |
| 167 | 191 | .only_section = only_section, |
| 168 | 192 | .pad_to = pad_to, |
| 169 | 193 | .add_section = add_section, |
| 194 | .set_section_alignment = set_section_alignment, |
| 195 | .set_section_flags = set_section_flags, |
| 170 | 196 | }); |
| 171 | 197 | }, |
| 172 | 198 | .elf => { |
| ... | ... | @@ -187,6 +213,8 @@ fn cmdObjCopy( |
| 187 | 213 | .extract_to = opt_extract, |
| 188 | 214 | .compress_debug = compress_debug_sections, |
| 189 | 215 | .add_section = add_section, |
| 216 | .set_section_alignment = set_section_alignment, |
| 217 | .set_section_flags = set_section_flags, |
| 190 | 218 | }); |
| 191 | 219 | return std.process.cleanExit(); |
| 192 | 220 | }, |
| ... | ... | @@ -229,19 +257,21 @@ const usage = |
| 229 | 257 | \\Usage: zig objcopy [options] input output |
| 230 | 258 | \\ |
| 231 | 259 | \\Options: |
| 232 | | \\ -h, --help Print this help and exit |
| 233 | | \\ --output-target=<value> Format of the output file |
| 234 | | \\ -O <value> Alias for --output-target |
| 235 | | \\ --only-section=<section> Remove all but <section> |
| 236 | | \\ -j <value> Alias for --only-section |
| 237 | | \\ --pad-to <addr> Pad the last section up to address <addr> |
| 238 | | \\ --strip-debug, -g Remove all debug sections from the output. |
| 239 | | \\ --strip-all, -S Remove all debug sections and symbol table from the output. |
| 240 | | \\ --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. |
| 241 | | \\ --add-gnu-debuglink=<file> Creates a .gnu_debuglink section which contains a reference to <file> and adds it to the output file. |
| 242 | | \\ --extract-to <file> Extract the removed sections into <file>, and add a .gnu-debuglink section. |
| 243 | | \\ --compress-debug-sections Compress DWARF debug sections with zlib |
| 244 | | \\ --add-section <name>=<file> Add file content from <file> with the section name <name>. |
| 260 | \\ -h, --help Print this help and exit |
| 261 | \\ --output-target=<value> Format of the output file |
| 262 | \\ -O <value> Alias for --output-target |
| 263 | \\ --only-section=<section> Remove all but <section> |
| 264 | \\ -j <value> Alias for --only-section |
| 265 | \\ --pad-to <addr> Pad the last section up to address <addr> |
| 266 | \\ --strip-debug, -g Remove all debug sections from the output. |
| 267 | \\ --strip-all, -S Remove all debug sections and symbol table from the output. |
| 268 | \\ --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. |
| 269 | \\ --add-gnu-debuglink=<file> Creates a .gnu_debuglink section which contains a reference to <file> and adds it to the output file. |
| 270 | \\ --extract-to <file> Extract the removed sections into <file>, and add a .gnu-debuglink section. |
| 271 | \\ --compress-debug-sections Compress DWARF debug sections with zlib |
| 272 | \\ --set-section-alignment <name>=<align> Set alignment of section <name> to <align> bytes. Must be a power of two. |
| 273 | \\ --set-section-flags <name>=<file> Set flags of section <name> to <flags> represented as a comma separated set of flags. |
| 274 | \\ --add-section <name>=<file> Add file content from <file> with the section name <name>. |
| 245 | 275 | \\ |
| 246 | 276 | ; |
| 247 | 277 | |
| ... | ... | @@ -250,11 +280,25 @@ pub const EmitRawElfOptions = struct { |
| 250 | 280 | only_section: ?[]const u8 = null, |
| 251 | 281 | pad_to: ?u64 = null, |
| 252 | 282 | add_section: ?AddSectionOptions, |
| 283 | set_section_alignment: ?SectionAlignmentOptions, |
| 284 | set_section_flags: ?SectionFlagsOptions, |
| 253 | 285 | }; |
| 254 | 286 | |
| 255 | 287 | const AddSectionOptions = struct { |
| 256 | 288 | section_name: []const u8, |
| 257 | | file: []const u8, |
| 289 | // file to store in new section |
| 290 | file_path: []const u8, |
| 291 | }; |
| 292 | |
| 293 | const SectionAlignmentOptions = struct { |
| 294 | section_name: []const u8, |
| 295 | alignment: u32, |
| 296 | }; |
| 297 | |
| 298 | const SectionFlagsOptions = struct { |
| 299 | section_name: []const u8, |
| 300 | // comma separated string representation of the SHF_x flags for Shdr.sh_flags |
| 301 | flags: []const u8, |
| 258 | 302 | }; |
| 259 | 303 | |
| 260 | 304 | fn emitElf( |
| ... | ... | @@ -698,6 +742,8 @@ const StripElfOptions = struct { |
| 698 | 742 | only_keep_debug: bool = false, |
| 699 | 743 | compress_debug: bool = false, |
| 700 | 744 | add_section: ?AddSectionOptions, |
| 745 | set_section_alignment: ?SectionAlignmentOptions, |
| 746 | set_section_flags: ?SectionFlagsOptions, |
| 701 | 747 | }; |
| 702 | 748 | |
| 703 | 749 | fn stripElf( |
| ... | ... | @@ -761,7 +807,14 @@ fn stripElf( |
| 761 | 807 | } |
| 762 | 808 | |
| 763 | 809 | const debuglink: ?DebugLink = if (debuglink_path) |path| ElfFileHelper.createDebugLink(path) else null; |
| 764 | | try elf_file.emit(allocator, out_file, in_file, .{ .section_filter = filter, .debuglink = debuglink, .compress_debug = options.compress_debug, .add_section = options.add_section }); |
| 810 | try elf_file.emit(allocator, out_file, in_file, .{ |
| 811 | .section_filter = filter, |
| 812 | .debuglink = debuglink, |
| 813 | .compress_debug = options.compress_debug, |
| 814 | .add_section = options.add_section, |
| 815 | .set_section_alignment = options.set_section_alignment, |
| 816 | .set_section_flags = options.set_section_flags, |
| 817 | }); |
| 765 | 818 | }, |
| 766 | 819 | } |
| 767 | 820 | } |
| ... | ... | @@ -925,6 +978,8 @@ fn ElfFile(comptime is_64: bool) type { |
| 925 | 978 | debuglink: ?DebugLink = null, |
| 926 | 979 | compress_debug: bool = false, |
| 927 | 980 | add_section: ?AddSectionOptions = null, |
| 981 | set_section_alignment: ?SectionAlignmentOptions = null, |
| 982 | set_section_flags: ?SectionFlagsOptions = null, |
| 928 | 983 | }; |
| 929 | 984 | fn emit(self: *const Self, gpa: Allocator, out_file: File, in_file: File, options: EmitElfOptions) !void { |
| 930 | 985 | var arena = std.heap.ArenaAllocator.init(gpa); |
| ... | ... | @@ -1191,14 +1246,19 @@ fn ElfFile(comptime is_64: bool) type { |
| 1191 | 1246 | |
| 1192 | 1247 | // add user section |
| 1193 | 1248 | if (options.add_section) |add_section| { |
| 1194 | | var section_file = fs.cwd().openFile(add_section.file, .{}) catch |err| |
| 1195 | | fatal("unable to open '{s}': {s}", .{ add_section.file, @errorName(err) }); |
| 1249 | var section_file = fs.cwd().openFile(add_section.file_path, .{}) catch |err| |
| 1250 | fatal("unable to open '{s}': {s}", .{ add_section.file_path, @errorName(err) }); |
| 1196 | 1251 | defer section_file.close(); |
| 1197 | 1252 | |
| 1198 | 1253 | const max_size = std.math.maxInt(usize); |
| 1199 | 1254 | const payload = try section_file.readToEndAlloc(arena.allocator(), max_size); |
| 1200 | 1255 | const flags = 0; // TODO: 19009: support --set-section-flags |
| 1201 | | const alignment = 4; // TODO: 19009: support --set-section-alignment |
| 1256 | const alignment = align_bytes: { |
| 1257 | if (options.set_section_alignment) |set_align| { |
| 1258 | if (std.mem.eql(u8, set_align.section_name, add_section.section_name)) break :align_bytes set_align.alignment; |
| 1259 | } |
| 1260 | break :align_bytes 4; |
| 1261 | }; |
| 1202 | 1262 | |
| 1203 | 1263 | dest_sections[dest_section_idx] = Elf_Shdr{ |
| 1204 | 1264 | .sh_name = user_section_name, |
| ... | ... | @@ -1218,6 +1278,16 @@ fn ElfFile(comptime is_64: bool) type { |
| 1218 | 1278 | eof_offset += @as(Elf_OffSize, @intCast(payload.len)); |
| 1219 | 1279 | } |
| 1220 | 1280 | |
| 1281 | // overwrite section alignment |
| 1282 | { |
| 1283 | // TODO: 19009: NYI |
| 1284 | } |
| 1285 | |
| 1286 | // overwrite section flags |
| 1287 | { |
| 1288 | // TODO: 19009: NYI |
| 1289 | } |
| 1290 | |
| 1221 | 1291 | assert(dest_section_idx == new_shnum); |
| 1222 | 1292 | break :blk dest_sections; |
| 1223 | 1293 | }; |
| ... | ... | @@ -1462,9 +1532,9 @@ fn splitOption(option: []const u8) ?SplitResult { |
| 1462 | 1532 | |
| 1463 | 1533 | test "Split option" { |
| 1464 | 1534 | { |
| 1465 | | const split = splitOption("a=123"); |
| 1535 | const split = splitOption(".abc=123"); |
| 1466 | 1536 | try std.testing.expect(split != null); |
| 1467 | | try std.testing.expectEqualStrings("a", split.?.first); |
| 1537 | try std.testing.expectEqualStrings(".abc", split.?.first); |
| 1468 | 1538 | try std.testing.expectEqualStrings("123", split.?.second); |
| 1469 | 1539 | } |
| 1470 | 1540 | |