authorgravatar for 35903594+patrickwick@users.noreply.github.comPatrick Wickenhaeuser <35903594+patrickwick@users.noreply.github.com> 2024-10-04 12:38:34+02:00
committergravatar for 35903594+patrickwick@users.noreply.github.comPatrick Wickenhaeuser <35903594+patrickwick@users.noreply.github.com> 2024-10-04 15:49:50+02:00
log172e7161a48658942c1428963448a68fa07467fa
tree795073037463e8979f1950d3b48a43351cfa214d
parentb23a5b56c2f4fcbba689d86055671fcb6297b7cc

19009: zig objcopy: add --add-section support


1 files changed, 113 insertions(+), 1 deletions(-)

lib/compiler/objcopy.zig+113-1
...@@ -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 zlib243 \\ --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;
234247
...@@ -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
255const AddSectionOptions = struct {
256 section_name: []const u8,
257 file: []const u8,
239};258};
240259
241fn emitElf(260fn 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};
682702
683fn stripElf(703fn 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();
723743
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 }
734762
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;
936965
966 if (options.add_section != null) {
967 next_idx += 1;
968 }
969
937 break :blk next_idx;970 break :blk next_idx;
938 };971 };
939972
...@@ -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 };
961994
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 sections1017 // 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 }
11361191
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
1449const SplitResult = struct { first: []const u8, second: []const u8 };
1450
1451fn 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
1463test "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}