| ... | ... | @@ -40,6 +40,7 @@ fn cmdObjCopy( |
| 40 | 40 | var only_keep_debug: bool = false; |
| 41 | 41 | var compress_debug_sections: bool = false; |
| 42 | 42 | var listen = false; |
| 43 | var add_section: ?AddSectionOptions = null; |
| 43 | 44 | while (i < args.len) : (i += 1) { |
| 44 | 45 | const arg = args[i]; |
| 45 | 46 | if (!mem.startsWith(u8, arg, "-")) { |
| ... | ... | @@ -104,6 +105,15 @@ fn cmdObjCopy( |
| 104 | 105 | i += 1; |
| 105 | 106 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); |
| 106 | 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 | 117 | } else { |
| 108 | 118 | fatal("unrecognized argument: '{s}'", .{arg}); |
| 109 | 119 | } |
| ... | ... | @@ -156,6 +166,7 @@ fn cmdObjCopy( |
| 156 | 166 | .ofmt = out_fmt, |
| 157 | 167 | .only_section = only_section, |
| 158 | 168 | .pad_to = pad_to, |
| 169 | .add_section = add_section, |
| 159 | 170 | }); |
| 160 | 171 | }, |
| 161 | 172 | .elf => { |
| ... | ... | @@ -175,6 +186,7 @@ fn cmdObjCopy( |
| 175 | 186 | .add_debuglink = opt_add_debuglink, |
| 176 | 187 | .extract_to = opt_extract, |
| 177 | 188 | .compress_debug = compress_debug_sections, |
| 189 | .add_section = add_section, |
| 178 | 190 | }); |
| 179 | 191 | return std.process.cleanExit(); |
| 180 | 192 | }, |
| ... | ... | @@ -229,6 +241,7 @@ const usage = |
| 229 | 241 | \\ --add-gnu-debuglink=<file> Creates a .gnu_debuglink section which contains a reference to <file> and adds it to the output file. |
| 230 | 242 | \\ --extract-to <file> Extract the removed sections into <file>, and add a .gnu-debuglink section. |
| 231 | 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 | 249 | ofmt: std.Target.ObjectFormat, |
| 237 | 250 | only_section: ?[]const u8 = null, |
| 238 | 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 | 260 | fn emitElf( |
| ... | ... | @@ -678,6 +697,7 @@ const StripElfOptions = struct { |
| 678 | 697 | strip_debug: bool = false, |
| 679 | 698 | only_keep_debug: bool = false, |
| 680 | 699 | compress_debug: bool = false, |
| 700 | add_section: ?AddSectionOptions, |
| 681 | 701 | }; |
| 682 | 702 | |
| 683 | 703 | fn stripElf( |
| ... | ... | @@ -721,6 +741,14 @@ fn stripElf( |
| 721 | 741 | var elf_file = try ElfFile(is_64).parse(allocator, in_file, elf_hdr); |
| 722 | 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 | 752 | if (filter_complement) |flt| { |
| 725 | 753 | // write the .dbg file and close it, so it can be read back to compute the debuglink checksum. |
| 726 | 754 | const path = options.extract_to.?; |
| ... | ... | @@ -733,7 +761,7 @@ fn stripElf( |
| 733 | 761 | } |
| 734 | 762 | |
| 735 | 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 | 924 | section_filter: Filter = .all, |
| 897 | 925 | debuglink: ?DebugLink = null, |
| 898 | 926 | compress_debug: bool = false, |
| 927 | add_section: ?AddSectionOptions = null, |
| 899 | 928 | }; |
| 900 | 929 | fn emit(self: *const Self, gpa: Allocator, out_file: File, in_file: File, options: EmitElfOptions) !void { |
| 901 | 930 | var arena = std.heap.ArenaAllocator.init(gpa); |
| ... | ... | @@ -934,6 +963,10 @@ fn ElfFile(comptime is_64: bool) type { |
| 934 | 963 | if (options.debuglink != null) |
| 935 | 964 | next_idx += 1; |
| 936 | 965 | |
| 966 | if (options.add_section != null) { |
| 967 | next_idx += 1; |
| 968 | } |
| 969 | |
| 937 | 970 | break :blk next_idx; |
| 938 | 971 | }; |
| 939 | 972 | |
| ... | ... | @@ -959,6 +992,28 @@ fn ElfFile(comptime is_64: bool) type { |
| 959 | 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 | 1017 | // maybe compress .debug sections |
| 963 | 1018 | if (options.compress_debug) { |
| 964 | 1019 | for (self.sections[1..], sections_update[1..]) |section, *update| { |
| ... | ... | @@ -1134,6 +1189,35 @@ fn ElfFile(comptime is_64: bool) type { |
| 1134 | 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 | 1221 | assert(dest_section_idx == new_shnum); |
| 1138 | 1222 | break :blk dest_sections; |
| 1139 | 1223 | }; |
| ... | ... | @@ -1361,3 +1445,31 @@ const ElfFileHelper = struct { |
| 1361 | 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 | } |