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(
4040 var only_keep_debug: bool = false;
4141 var compress_debug_sections: bool = false;
4242 var listen = false;
43 var add_section: ?AddSectionOptions = null;
4344 while (i < args.len) : (i += 1) {
4445 const arg = args[i];
4546 if (!mem.startsWith(u8, arg, "-")) {
......@@ -104,6 +105,15 @@ fn cmdObjCopy(
104105 i += 1;
105106 if (i >= args.len) fatal("expected another argument after '{s}'", .{arg});
106107 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 }
107117 } else {
108118 fatal("unrecognized argument: '{s}'", .{arg});
109119 }
......@@ -156,6 +166,7 @@ fn cmdObjCopy(
156166 .ofmt = out_fmt,
157167 .only_section = only_section,
158168 .pad_to = pad_to,
169 .add_section = add_section,
159170 });
160171 },
161172 .elf => {
......@@ -175,6 +186,7 @@ fn cmdObjCopy(
175186 .add_debuglink = opt_add_debuglink,
176187 .extract_to = opt_extract,
177188 .compress_debug = compress_debug_sections,
189 .add_section = add_section,
178190 });
179191 return std.process.cleanExit();
180192 },
......@@ -229,6 +241,7 @@ const usage =
229241 \\ --add-gnu-debuglink=<file> Creates a .gnu_debuglink section which contains a reference to <file> and adds it to the output file.
230242 \\ --extract-to <file> Extract the removed sections into <file>, and add a .gnu-debuglink section.
231243 \\ --compress-debug-sections Compress DWARF debug sections with zlib
244 \\ --add-section <name>=<file> Add file content from <file> with the section name <name>.
232245 \\
233246;
234247
......@@ -236,6 +249,12 @@ pub const EmitRawElfOptions = struct {
236249 ofmt: std.Target.ObjectFormat,
237250 only_section: ?[]const u8 = null,
238251 pad_to: ?u64 = null,
252 add_section: ?AddSectionOptions,
253};
254
255const AddSectionOptions = struct {
256 section_name: []const u8,
257 file: []const u8,
239258};
240259
241260fn emitElf(
......@@ -678,6 +697,7 @@ const StripElfOptions = struct {
678697 strip_debug: bool = false,
679698 only_keep_debug: bool = false,
680699 compress_debug: bool = false,
700 add_section: ?AddSectionOptions,
681701};
682702
683703fn stripElf(
......@@ -721,6 +741,14 @@ fn stripElf(
721741 var elf_file = try ElfFile(is_64).parse(allocator, in_file, elf_hdr);
722742 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
724752 if (filter_complement) |flt| {
725753 // write the .dbg file and close it, so it can be read back to compute the debuglink checksum.
726754 const path = options.extract_to.?;
......@@ -733,7 +761,7 @@ fn stripElf(
733761 }
734762
735763 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 });
737765 },
738766 }
739767}
......@@ -896,6 +924,7 @@ fn ElfFile(comptime is_64: bool) type {
896924 section_filter: Filter = .all,
897925 debuglink: ?DebugLink = null,
898926 compress_debug: bool = false,
927 add_section: ?AddSectionOptions = null,
899928 };
900929 fn emit(self: *const Self, gpa: Allocator, out_file: File, in_file: File, options: EmitElfOptions) !void {
901930 var arena = std.heap.ArenaAllocator.init(gpa);
......@@ -934,6 +963,10 @@ fn ElfFile(comptime is_64: bool) type {
934963 if (options.debuglink != null)
935964 next_idx += 1;
936965
966 if (options.add_section != null) {
967 next_idx += 1;
968 }
969
937970 break :blk next_idx;
938971 };
939972
......@@ -959,6 +992,28 @@ fn ElfFile(comptime is_64: bool) type {
959992 break :blk new_offset;
960993 };
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
9621017 // maybe compress .debug sections
9631018 if (options.compress_debug) {
9641019 for (self.sections[1..], sections_update[1..]) |section, *update| {
......@@ -1134,6 +1189,35 @@ fn ElfFile(comptime is_64: bool) type {
11341189 eof_offset += @as(Elf_OffSize, @intCast(payload.len));
11351190 }
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
11371221 assert(dest_section_idx == new_shnum);
11381222 break :blk dest_sections;
11391223 };
......@@ -1361,3 +1445,31 @@ const ElfFileHelper = struct {
13611445 return hasher.final();
13621446 }
13631447};
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}