| ... | @@ -41,6 +41,8 @@ fn cmdObjCopy( | ... | @@ -41,6 +41,8 @@ fn cmdObjCopy( |
| 41 | var compress_debug_sections: bool = false; | 41 | var compress_debug_sections: bool = false; |
| 42 | var listen = false; | 42 | var listen = false; |
| 43 | var add_section: ?AddSectionOptions = null; | 43 | var add_section: ?AddSectionOptions = null; |
| | 44 | var set_section_alignment: ?SectionAlignmentOptions = null; |
| | 45 | var set_section_flags: ?SectionFlagsOptions = null; |
| 44 | while (i < args.len) : (i += 1) { | 46 | while (i < args.len) : (i += 1) { |
| 45 | const arg = args[i]; | 47 | const arg = args[i]; |
| 46 | if (!mem.startsWith(u8, arg, "-")) { | 48 | if (!mem.startsWith(u8, arg, "-")) { |
| ... | @@ -105,12 +107,34 @@ fn cmdObjCopy( | ... | @@ -105,12 +107,34 @@ fn cmdObjCopy( |
| 105 | i += 1; | 107 | i += 1; |
| 106 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); | 108 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); |
| 107 | opt_extract = args[i]; | 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 | } else if (mem.eql(u8, arg, "--add-section")) { | 132 | } else if (mem.eql(u8, arg, "--add-section")) { |
| 109 | i += 1; | 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 | if (splitOption(args[i])) |split| { | 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 | } else { | 138 | } else { |
| 115 | fatal("unrecognized argument: '{s}', expecting <name>=<file>", .{args[i]}); | 139 | fatal("unrecognized argument: '{s}', expecting <name>=<file>", .{args[i]}); |
| 116 | } | 140 | } |
| ... | @@ -167,6 +191,8 @@ fn cmdObjCopy( | ... | @@ -167,6 +191,8 @@ fn cmdObjCopy( |
| 167 | .only_section = only_section, | 191 | .only_section = only_section, |
| 168 | .pad_to = pad_to, | 192 | .pad_to = pad_to, |
| 169 | .add_section = add_section, | 193 | .add_section = add_section, |
| | 194 | .set_section_alignment = set_section_alignment, |
| | 195 | .set_section_flags = set_section_flags, |
| 170 | }); | 196 | }); |
| 171 | }, | 197 | }, |
| 172 | .elf => { | 198 | .elf => { |
| ... | @@ -187,6 +213,8 @@ fn cmdObjCopy( | ... | @@ -187,6 +213,8 @@ fn cmdObjCopy( |
| 187 | .extract_to = opt_extract, | 213 | .extract_to = opt_extract, |
| 188 | .compress_debug = compress_debug_sections, | 214 | .compress_debug = compress_debug_sections, |
| 189 | .add_section = add_section, | 215 | .add_section = add_section, |
| | 216 | .set_section_alignment = set_section_alignment, |
| | 217 | .set_section_flags = set_section_flags, |
| 190 | }); | 218 | }); |
| 191 | return std.process.cleanExit(); | 219 | return std.process.cleanExit(); |
| 192 | }, | 220 | }, |
| ... | @@ -229,19 +257,21 @@ const usage = | ... | @@ -229,19 +257,21 @@ const usage = |
| 229 | \\Usage: zig objcopy [options] input output | 257 | \\Usage: zig objcopy [options] input output |
| 230 | \\ | 258 | \\ |
| 231 | \\Options: | 259 | \\Options: |
| 232 | \\ -h, --help Print this help and exit | 260 | \\ -h, --help Print this help and exit |
| 233 | \\ --output-target=<value> Format of the output file | 261 | \\ --output-target=<value> Format of the output file |
| 234 | \\ -O <value> Alias for --output-target | 262 | \\ -O <value> Alias for --output-target |
| 235 | \\ --only-section=<section> Remove all but <section> | 263 | \\ --only-section=<section> Remove all but <section> |
| 236 | \\ -j <value> Alias for --only-section | 264 | \\ -j <value> Alias for --only-section |
| 237 | \\ --pad-to <addr> Pad the last section up to address <addr> | 265 | \\ --pad-to <addr> Pad the last section up to address <addr> |
| 238 | \\ --strip-debug, -g Remove all debug sections from the output. | 266 | \\ --strip-debug, -g Remove all debug sections from the output. |
| 239 | \\ --strip-all, -S Remove all debug sections and symbol table from the output. | 267 | \\ --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. | 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. |
| 241 | \\ --add-gnu-debuglink=<file> Creates a .gnu_debuglink section which contains a reference to <file> and adds it to the output file. | 269 | \\ --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. | 270 | \\ --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 | 271 | \\ --compress-debug-sections Compress DWARF debug sections with zlib |
| 244 | \\ --add-section <name>=<file> Add file content from <file> with the section name <name>. | 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,11 +280,25 @@ pub const EmitRawElfOptions = struct { |
| 250 | only_section: ?[]const u8 = null, | 280 | only_section: ?[]const u8 = null, |
| 251 | pad_to: ?u64 = null, | 281 | pad_to: ?u64 = null, |
| 252 | add_section: ?AddSectionOptions, | 282 | add_section: ?AddSectionOptions, |
| | 283 | set_section_alignment: ?SectionAlignmentOptions, |
| | 284 | set_section_flags: ?SectionFlagsOptions, |
| 253 | }; | 285 | }; |
| 254 | | 286 | |
| 255 | const AddSectionOptions = struct { | 287 | const AddSectionOptions = struct { |
| 256 | section_name: []const u8, | 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 | fn emitElf( | 304 | fn emitElf( |
| ... | @@ -698,6 +742,8 @@ const StripElfOptions = struct { | ... | @@ -698,6 +742,8 @@ const StripElfOptions = struct { |
| 698 | only_keep_debug: bool = false, | 742 | only_keep_debug: bool = false, |
| 699 | compress_debug: bool = false, | 743 | compress_debug: bool = false, |
| 700 | add_section: ?AddSectionOptions, | 744 | add_section: ?AddSectionOptions, |
| | 745 | set_section_alignment: ?SectionAlignmentOptions, |
| | 746 | set_section_flags: ?SectionFlagsOptions, |
| 701 | }; | 747 | }; |
| 702 | | 748 | |
| 703 | fn stripElf( | 749 | fn stripElf( |
| ... | @@ -761,7 +807,14 @@ fn stripElf( | ... | @@ -761,7 +807,14 @@ fn stripElf( |
| 761 | } | 807 | } |
| 762 | | 808 | |
| 763 | const debuglink: ?DebugLink = if (debuglink_path) |path| ElfFileHelper.createDebugLink(path) else null; | 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,6 +978,8 @@ fn ElfFile(comptime is_64: bool) type { |
| 925 | debuglink: ?DebugLink = null, | 978 | debuglink: ?DebugLink = null, |
| 926 | compress_debug: bool = false, | 979 | compress_debug: bool = false, |
| 927 | add_section: ?AddSectionOptions = null, | 980 | add_section: ?AddSectionOptions = null, |
| | 981 | set_section_alignment: ?SectionAlignmentOptions = null, |
| | 982 | set_section_flags: ?SectionFlagsOptions = null, |
| 928 | }; | 983 | }; |
| 929 | fn emit(self: *const Self, gpa: Allocator, out_file: File, in_file: File, options: EmitElfOptions) !void { | 984 | fn emit(self: *const Self, gpa: Allocator, out_file: File, in_file: File, options: EmitElfOptions) !void { |
| 930 | var arena = std.heap.ArenaAllocator.init(gpa); | 985 | var arena = std.heap.ArenaAllocator.init(gpa); |
| ... | @@ -1191,14 +1246,19 @@ fn ElfFile(comptime is_64: bool) type { | ... | @@ -1191,14 +1246,19 @@ fn ElfFile(comptime is_64: bool) type { |
| 1191 | | 1246 | |
| 1192 | // add user section | 1247 | // add user section |
| 1193 | if (options.add_section) |add_section| { | 1248 | if (options.add_section) |add_section| { |
| 1194 | var section_file = fs.cwd().openFile(add_section.file, .{}) catch |err| | 1249 | var section_file = fs.cwd().openFile(add_section.file_path, .{}) catch |err| |
| 1195 | fatal("unable to open '{s}': {s}", .{ add_section.file, @errorName(err) }); | 1250 | fatal("unable to open '{s}': {s}", .{ add_section.file_path, @errorName(err) }); |
| 1196 | defer section_file.close(); | 1251 | defer section_file.close(); |
| 1197 | | 1252 | |
| 1198 | const max_size = std.math.maxInt(usize); | 1253 | const max_size = std.math.maxInt(usize); |
| 1199 | const payload = try section_file.readToEndAlloc(arena.allocator(), max_size); | 1254 | const payload = try section_file.readToEndAlloc(arena.allocator(), max_size); |
| 1200 | const flags = 0; // TODO: 19009: support --set-section-flags | 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 | dest_sections[dest_section_idx] = Elf_Shdr{ | 1263 | dest_sections[dest_section_idx] = Elf_Shdr{ |
| 1204 | .sh_name = user_section_name, | 1264 | .sh_name = user_section_name, |
| ... | @@ -1218,6 +1278,16 @@ fn ElfFile(comptime is_64: bool) type { | ... | @@ -1218,6 +1278,16 @@ fn ElfFile(comptime is_64: bool) type { |
| 1218 | eof_offset += @as(Elf_OffSize, @intCast(payload.len)); | 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 | assert(dest_section_idx == new_shnum); | 1291 | assert(dest_section_idx == new_shnum); |
| 1222 | break :blk dest_sections; | 1292 | break :blk dest_sections; |
| 1223 | }; | 1293 | }; |
| ... | @@ -1462,9 +1532,9 @@ fn splitOption(option: []const u8) ?SplitResult { | ... | @@ -1462,9 +1532,9 @@ fn splitOption(option: []const u8) ?SplitResult { |
| 1462 | | 1532 | |
| 1463 | test "Split option" { | 1533 | test "Split option" { |
| 1464 | { | 1534 | { |
| 1465 | const split = splitOption("a=123"); | 1535 | const split = splitOption(".abc=123"); |
| 1466 | try std.testing.expect(split != null); | 1536 | try std.testing.expect(split != null); |
| 1467 | try std.testing.expectEqualStrings("a", split.?.first); | 1537 | try std.testing.expectEqualStrings(".abc", split.?.first); |
| 1468 | try std.testing.expectEqualStrings("123", split.?.second); | 1538 | try std.testing.expectEqualStrings("123", split.?.second); |
| 1469 | } | 1539 | } |
| 1470 | | 1540 | |