| ... | @@ -40,6 +40,7 @@ fn cmdObjCopy( | ... | @@ -40,6 +40,7 @@ fn cmdObjCopy( |
| 40 | var only_keep_debug: bool = false; | 40 | var only_keep_debug: bool = false; |
| 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 | while (i < args.len) : (i += 1) { | 44 | while (i < args.len) : (i += 1) { |
| 44 | const arg = args[i]; | 45 | const arg = args[i]; |
| 45 | if (!mem.startsWith(u8, arg, "-")) { | 46 | if (!mem.startsWith(u8, arg, "-")) { |
| ... | @@ -104,6 +105,15 @@ fn cmdObjCopy( | ... | @@ -104,6 +105,15 @@ fn cmdObjCopy( |
| 104 | i += 1; | 105 | i += 1; |
| 105 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); | 106 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); |
| 106 | opt_extract = args[i]; | 107 | opt_extract = args[i]; |
| | 108 | } else if (mem.eql(u8, arg, "--add-section")) { |
| | 109 | i += 1; |
| | 110 | if (i >= args.len) fatal("expected name and filename arguments after '{s}'", .{arg}); |
| | 111 | |
| | 112 | if (splitOption(args[i])) |split| { |
| | 113 | add_section = .{ .section_name = split.first, .file = split.second }; |
| | 114 | } else { |
| | 115 | fatal("unrecognized argument: '{s}', expecting <name>=<file>", .{args[i]}); |
| | 116 | } |
| 107 | } else { | 117 | } else { |
| 108 | fatal("unrecognized argument: '{s}'", .{arg}); | 118 | fatal("unrecognized argument: '{s}'", .{arg}); |
| 109 | } | 119 | } |
| ... | @@ -156,6 +166,7 @@ fn cmdObjCopy( | ... | @@ -156,6 +166,7 @@ fn cmdObjCopy( |
| 156 | .ofmt = out_fmt, | 166 | .ofmt = out_fmt, |
| 157 | .only_section = only_section, | 167 | .only_section = only_section, |
| 158 | .pad_to = pad_to, | 168 | .pad_to = pad_to, |
| | 169 | .add_section = add_section, |
| 159 | }); | 170 | }); |
| 160 | }, | 171 | }, |
| 161 | .elf => { | 172 | .elf => { |
| ... | @@ -175,6 +186,7 @@ fn cmdObjCopy( | ... | @@ -175,6 +186,7 @@ fn cmdObjCopy( |
| 175 | .add_debuglink = opt_add_debuglink, | 186 | .add_debuglink = opt_add_debuglink, |
| 176 | .extract_to = opt_extract, | 187 | .extract_to = opt_extract, |
| 177 | .compress_debug = compress_debug_sections, | 188 | .compress_debug = compress_debug_sections, |
| | 189 | .add_section = add_section, |
| 178 | }); | 190 | }); |
| 179 | return std.process.cleanExit(); | 191 | return std.process.cleanExit(); |
| 180 | }, | 192 | }, |
| ... | @@ -229,6 +241,7 @@ const usage = | ... | @@ -229,6 +241,7 @@ const usage = |
| 229 | \\ --add-gnu-debuglink=<file> Creates a .gnu_debuglink section which contains a reference to <file> and adds it to the output file. | 241 | \\ --add-gnu-debuglink=<file> Creates a .gnu_debuglink section which contains a reference to <file> and adds it to the output file. |
| 230 | \\ --extract-to <file> Extract the removed sections into <file>, and add a .gnu-debuglink section. | 242 | \\ --extract-to <file> Extract the removed sections into <file>, and add a .gnu-debuglink section. |
| 231 | \\ --compress-debug-sections Compress DWARF debug sections with zlib | 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>. |
| 232 | \\ | 245 | \\ |
| 233 | ; | 246 | ; |
| 234 | | 247 | |
| ... | @@ -236,6 +249,12 @@ pub const EmitRawElfOptions = struct { | ... | @@ -236,6 +249,12 @@ pub const EmitRawElfOptions = struct { |
| 236 | ofmt: std.Target.ObjectFormat, | 249 | ofmt: std.Target.ObjectFormat, |
| 237 | only_section: ?[]const u8 = null, | 250 | only_section: ?[]const u8 = null, |
| 238 | pad_to: ?u64 = null, | 251 | pad_to: ?u64 = null, |
| | 252 | add_section: ?AddSectionOptions, |
| | 253 | }; |
| | 254 | |
| | 255 | const AddSectionOptions = struct { |
| | 256 | section_name: []const u8, |
| | 257 | file: []const u8, |
| 239 | }; | 258 | }; |
| 240 | | 259 | |
| 241 | fn emitElf( | 260 | fn emitElf( |
| ... | @@ -678,6 +697,7 @@ const StripElfOptions = struct { | ... | @@ -678,6 +697,7 @@ const StripElfOptions = struct { |
| 678 | strip_debug: bool = false, | 697 | strip_debug: bool = false, |
| 679 | only_keep_debug: bool = false, | 698 | only_keep_debug: bool = false, |
| 680 | compress_debug: bool = false, | 699 | compress_debug: bool = false, |
| | 700 | add_section: ?AddSectionOptions, |
| 681 | }; | 701 | }; |
| 682 | | 702 | |
| 683 | fn stripElf( | 703 | fn stripElf( |
| ... | @@ -721,6 +741,14 @@ fn stripElf( | ... | @@ -721,6 +741,14 @@ fn stripElf( |
| 721 | var elf_file = try ElfFile(is_64).parse(allocator, in_file, elf_hdr); | 741 | var elf_file = try ElfFile(is_64).parse(allocator, in_file, elf_hdr); |
| 722 | defer elf_file.deinit(); | 742 | defer elf_file.deinit(); |
| 723 | | 743 | |
| | 744 | if (options.add_section) |user_section| { |
| | 745 | for (elf_file.sections) |section| { |
| | 746 | if (std.mem.eql(u8, section.name, user_section.section_name)) { |
| | 747 | fatal("zig objcopy: unable to add section '{s}'. Section already exists in input", .{user_section.section_name}); |
| | 748 | } |
| | 749 | } |
| | 750 | } |
| | 751 | |
| 724 | if (filter_complement) |flt| { | 752 | if (filter_complement) |flt| { |
| 725 | // write the .dbg file and close it, so it can be read back to compute the debuglink checksum. | 753 | // write the .dbg file and close it, so it can be read back to compute the debuglink checksum. |
| 726 | const path = options.extract_to.?; | 754 | const path = options.extract_to.?; |
| ... | @@ -733,7 +761,7 @@ fn stripElf( | ... | @@ -733,7 +761,7 @@ fn stripElf( |
| 733 | } | 761 | } |
| 734 | | 762 | |
| 735 | const debuglink: ?DebugLink = if (debuglink_path) |path| ElfFileHelper.createDebugLink(path) else null; | 763 | const debuglink: ?DebugLink = if (debuglink_path) |path| ElfFileHelper.createDebugLink(path) else null; |
| 736 | try elf_file.emit(allocator, out_file, in_file, .{ .section_filter = filter, .debuglink = debuglink, .compress_debug = options.compress_debug }); | 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 }); |
| 737 | }, | 765 | }, |
| 738 | } | 766 | } |
| 739 | } | 767 | } |
| ... | @@ -896,6 +924,7 @@ fn ElfFile(comptime is_64: bool) type { | ... | @@ -896,6 +924,7 @@ fn ElfFile(comptime is_64: bool) type { |
| 896 | section_filter: Filter = .all, | 924 | section_filter: Filter = .all, |
| 897 | debuglink: ?DebugLink = null, | 925 | debuglink: ?DebugLink = null, |
| 898 | compress_debug: bool = false, | 926 | compress_debug: bool = false, |
| | 927 | add_section: ?AddSectionOptions = null, |
| 899 | }; | 928 | }; |
| 900 | fn emit(self: *const Self, gpa: Allocator, out_file: File, in_file: File, options: EmitElfOptions) !void { | 929 | fn emit(self: *const Self, gpa: Allocator, out_file: File, in_file: File, options: EmitElfOptions) !void { |
| 901 | var arena = std.heap.ArenaAllocator.init(gpa); | 930 | var arena = std.heap.ArenaAllocator.init(gpa); |
| ... | @@ -934,6 +963,10 @@ fn ElfFile(comptime is_64: bool) type { | ... | @@ -934,6 +963,10 @@ fn ElfFile(comptime is_64: bool) type { |
| 934 | if (options.debuglink != null) | 963 | if (options.debuglink != null) |
| 935 | next_idx += 1; | 964 | next_idx += 1; |
| 936 | | 965 | |
| | 966 | if (options.add_section != null) { |
| | 967 | next_idx += 1; |
| | 968 | } |
| | 969 | |
| 937 | break :blk next_idx; | 970 | break :blk next_idx; |
| 938 | }; | 971 | }; |
| 939 | | 972 | |
| ... | @@ -959,6 +992,28 @@ fn ElfFile(comptime is_64: bool) type { | ... | @@ -959,6 +992,28 @@ fn ElfFile(comptime is_64: bool) type { |
| 959 | break :blk new_offset; | 992 | break :blk new_offset; |
| 960 | }; | 993 | }; |
| 961 | | 994 | |
| | 995 | // add user section to the string table if needed |
| | 996 | const user_section_name: u32 = blk: { |
| | 997 | if (options.add_section == null) break :blk elf.SHN_UNDEF; |
| | 998 | if (self.raw_elf_header.e_shstrndx == elf.SHN_UNDEF) |
| | 999 | fatal("zig objcopy: no strtab, cannot add the user section", .{}); // TODO add the section if needed? |
| | 1000 | |
| | 1001 | const strtab = &self.sections[self.raw_elf_header.e_shstrndx]; |
| | 1002 | const update = &sections_update[self.raw_elf_header.e_shstrndx]; |
| | 1003 | |
| | 1004 | const name = options.add_section.?.section_name; |
| | 1005 | const new_offset: u32 = @intCast(strtab.payload.?.len); |
| | 1006 | const buf = try allocator.alignedAlloc(u8, section_memory_align, new_offset + name.len + 1); |
| | 1007 | @memcpy(buf[0..new_offset], strtab.payload.?); |
| | 1008 | @memcpy(buf[new_offset..][0..name.len], name); |
| | 1009 | buf[new_offset + name.len] = 0; |
| | 1010 | |
| | 1011 | assert(update.action == .keep); |
| | 1012 | update.payload = buf; |
| | 1013 | |
| | 1014 | break :blk new_offset; |
| | 1015 | }; |
| | 1016 | |
| 962 | // maybe compress .debug sections | 1017 | // maybe compress .debug sections |
| 963 | if (options.compress_debug) { | 1018 | if (options.compress_debug) { |
| 964 | for (self.sections[1..], sections_update[1..]) |section, *update| { | 1019 | for (self.sections[1..], sections_update[1..]) |section, *update| { |
| ... | @@ -1134,6 +1189,35 @@ fn ElfFile(comptime is_64: bool) type { | ... | @@ -1134,6 +1189,35 @@ fn ElfFile(comptime is_64: bool) type { |
| 1134 | eof_offset += @as(Elf_OffSize, @intCast(payload.len)); | 1189 | eof_offset += @as(Elf_OffSize, @intCast(payload.len)); |
| 1135 | } | 1190 | } |
| 1136 | | 1191 | |
| | 1192 | // add user section |
| | 1193 | 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) }); |
| | 1196 | defer section_file.close(); |
| | 1197 | |
| | 1198 | const max_size = std.math.maxInt(usize); |
| | 1199 | const payload = try section_file.readToEndAlloc(arena.allocator(), max_size); |
| | 1200 | const flags = 0; // TODO: 19009: support --set-section-flags |
| | 1201 | const alignment = 4; // TODO: 19009: support --set-section-alignment |
| | 1202 | |
| | 1203 | dest_sections[dest_section_idx] = Elf_Shdr{ |
| | 1204 | .sh_name = user_section_name, |
| | 1205 | .sh_type = elf.SHT_PROGBITS, |
| | 1206 | .sh_flags = flags, |
| | 1207 | .sh_addr = 0, |
| | 1208 | .sh_offset = eof_offset, |
| | 1209 | .sh_size = @intCast(payload.len), |
| | 1210 | .sh_link = elf.SHN_UNDEF, |
| | 1211 | .sh_info = elf.SHN_UNDEF, |
| | 1212 | .sh_addralign = alignment, |
| | 1213 | .sh_entsize = 0, |
| | 1214 | }; |
| | 1215 | dest_section_idx += 1; |
| | 1216 | |
| | 1217 | cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = payload, .out_offset = eof_offset } }); |
| | 1218 | eof_offset += @as(Elf_OffSize, @intCast(payload.len)); |
| | 1219 | } |
| | 1220 | |
| 1137 | assert(dest_section_idx == new_shnum); | 1221 | assert(dest_section_idx == new_shnum); |
| 1138 | break :blk dest_sections; | 1222 | break :blk dest_sections; |
| 1139 | }; | 1223 | }; |
| ... | @@ -1361,3 +1445,31 @@ const ElfFileHelper = struct { | ... | @@ -1361,3 +1445,31 @@ const ElfFileHelper = struct { |
| 1361 | return hasher.final(); | 1445 | return hasher.final(); |
| 1362 | } | 1446 | } |
| 1363 | }; | 1447 | }; |
| | 1448 | |
| | 1449 | const SplitResult = struct { first: []const u8, second: []const u8 }; |
| | 1450 | |
| | 1451 | fn splitOption(option: []const u8) ?SplitResult { |
| | 1452 | const separator = '='; |
| | 1453 | if (option.len < 3) return null; // minimum "a=b" |
| | 1454 | for (1..option.len - 1) |i| { |
| | 1455 | if (option[i] == separator) return .{ |
| | 1456 | .first = option[0..i], |
| | 1457 | .second = option[i + 1 ..], |
| | 1458 | }; |
| | 1459 | } |
| | 1460 | return null; |
| | 1461 | } |
| | 1462 | |
| | 1463 | test "Split option" { |
| | 1464 | { |
| | 1465 | const split = splitOption("a=123"); |
| | 1466 | try std.testing.expect(split != null); |
| | 1467 | try std.testing.expectEqualStrings("a", split.?.first); |
| | 1468 | try std.testing.expectEqualStrings("123", split.?.second); |
| | 1469 | } |
| | 1470 | |
| | 1471 | try std.testing.expectEqual(null, splitOption("")); |
| | 1472 | try std.testing.expectEqual(null, splitOption("=abc")); |
| | 1473 | try std.testing.expectEqual(null, splitOption("abc=")); |
| | 1474 | try std.testing.expectEqual(null, splitOption("abc")); |
| | 1475 | } |