authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-04 19:45:39-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-10-04 19:45:39-07:00
logcfd3bcffec997a18521f685738e4dc7eef940422
treee185f7266975d7f04b2b0287d0e58143ff637a22
parent98b85f72a656ef62a23cf0d0fbc3ed3b5cecc95b
parentc3f1ff8b0d2aa6fa94854775b80d57c251ce1438
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21591 from patrickwick/issue-19009

zig objcopy: support --add-section

2 files changed, 405 insertions(+), 16 deletions(-)

lib/compiler/objcopy.zig+325-16
......@@ -40,6 +40,9 @@ fn cmdObjCopy(
4040 var only_keep_debug: bool = false;
4141 var compress_debug_sections: bool = false;
4242 var listen = false;
43 var add_section: ?AddSection = null;
44 var set_section_alignment: ?SetSectionAlignment = null;
45 var set_section_flags: ?SetSectionFlags = null;
4346 while (i < args.len) : (i += 1) {
4447 const arg = args[i];
4548 if (!mem.startsWith(u8, arg, "-")) {
......@@ -104,6 +107,37 @@ fn cmdObjCopy(
104107 i += 1;
105108 if (i >= args.len) fatal("expected another argument after '{s}'", .{arg});
106109 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 = parseSectionFlags(split.second) };
129 } else {
130 fatal("unrecognized argument: '{s}', expecting <name>=<flags>", .{args[i]});
131 }
132 } else if (mem.eql(u8, arg, "--add-section")) {
133 i += 1;
134 if (i >= args.len) fatal("expected section name and filename arguments after '{s}'", .{arg});
135
136 if (splitOption(args[i])) |split| {
137 add_section = .{ .section_name = split.first, .file_path = split.second };
138 } else {
139 fatal("unrecognized argument: '{s}', expecting <name>=<file>", .{args[i]});
140 }
107141 } else {
108142 fatal("unrecognized argument: '{s}'", .{arg});
109143 }
......@@ -151,6 +185,12 @@ fn cmdObjCopy(
151185 fatal("zig objcopy: ELF to RAW or HEX copying does not support --strip", .{});
152186 if (opt_extract != null)
153187 fatal("zig objcopy: ELF to RAW or HEX copying does not support --extract-to", .{});
188 if (add_section != null)
189 fatal("zig objcopy: ELF to RAW or HEX copying does not support --add-section", .{});
190 if (set_section_alignment != null)
191 fatal("zig objcopy: ELF to RAW or HEX copying does not support --set_section_alignment", .{});
192 if (set_section_flags != null)
193 fatal("zig objcopy: ELF to RAW or HEX copying does not support --set_section_flags", .{});
154194
155195 try emitElf(arena, in_file, out_file, elf_hdr, .{
156196 .ofmt = out_fmt,
......@@ -175,6 +215,9 @@ fn cmdObjCopy(
175215 .add_debuglink = opt_add_debuglink,
176216 .extract_to = opt_extract,
177217 .compress_debug = compress_debug_sections,
218 .add_section = add_section,
219 .set_section_alignment = set_section_alignment,
220 .set_section_flags = set_section_flags,
178221 });
179222 return std.process.cleanExit();
180223 },
......@@ -217,18 +260,21 @@ const usage =
217260 \\Usage: zig objcopy [options] input output
218261 \\
219262 \\Options:
220 \\ -h, --help Print this help and exit
221 \\ --output-target=<value> Format of the output file
222 \\ -O <value> Alias for --output-target
223 \\ --only-section=<section> Remove all but <section>
224 \\ -j <value> Alias for --only-section
225 \\ --pad-to <addr> Pad the last section up to address <addr>
226 \\ --strip-debug, -g Remove all debug sections from the output.
227 \\ --strip-all, -S Remove all debug sections and symbol table from the output.
228 \\ --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.
229 \\ --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.
231 \\ --compress-debug-sections Compress DWARF debug sections with zlib
263 \\ -h, --help Print this help and exit
264 \\ --output-target=<value> Format of the output file
265 \\ -O <value> Alias for --output-target
266 \\ --only-section=<section> Remove all but <section>
267 \\ -j <value> Alias for --only-section
268 \\ --pad-to <addr> Pad the last section up to address <addr>
269 \\ --strip-debug, -g Remove all debug sections from the output.
270 \\ --strip-all, -S Remove all debug sections and symbol table from the output.
271 \\ --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.
272 \\ --add-gnu-debuglink=<file> Creates a .gnu_debuglink section which contains a reference to <file> and adds it to the output file.
273 \\ --extract-to <file> Extract the removed sections into <file>, and add a .gnu-debuglink section.
274 \\ --compress-debug-sections Compress DWARF debug sections with zlib
275 \\ --set-section-alignment <name>=<align> Set alignment of section <name> to <align> bytes. Must be a power of two.
276 \\ --set-section-flags <name>=<file> Set flags of section <name> to <flags> represented as a comma separated set of flags.
277 \\ --add-section <name>=<file> Add file content from <file> with the a new section named <name>.
232278 \\
233279;
234280
......@@ -236,6 +282,24 @@ pub const EmitRawElfOptions = struct {
236282 ofmt: std.Target.ObjectFormat,
237283 only_section: ?[]const u8 = null,
238284 pad_to: ?u64 = null,
285 add_section: ?AddSection = null,
286 set_section_alignment: ?SetSectionAlignment = null,
287 set_section_flags: ?SetSectionFlags = null,
288};
289
290const AddSection = struct {
291 section_name: []const u8,
292 file_path: []const u8,
293};
294
295const SetSectionAlignment = struct {
296 section_name: []const u8,
297 alignment: u32,
298};
299
300const SetSectionFlags = struct {
301 section_name: []const u8,
302 flags: SectionFlags,
239303};
240304
241305fn emitElf(
......@@ -678,6 +742,9 @@ const StripElfOptions = struct {
678742 strip_debug: bool = false,
679743 only_keep_debug: bool = false,
680744 compress_debug: bool = false,
745 add_section: ?AddSection,
746 set_section_alignment: ?SetSectionAlignment,
747 set_section_flags: ?SetSectionFlags,
681748};
682749
683750fn stripElf(
......@@ -721,6 +788,14 @@ fn stripElf(
721788 var elf_file = try ElfFile(is_64).parse(allocator, in_file, elf_hdr);
722789 defer elf_file.deinit();
723790
791 if (options.add_section) |user_section| {
792 for (elf_file.sections) |section| {
793 if (std.mem.eql(u8, section.name, user_section.section_name)) {
794 fatal("zig objcopy: unable to add section '{s}'. Section already exists in input", .{user_section.section_name});
795 }
796 }
797 }
798
724799 if (filter_complement) |flt| {
725800 // write the .dbg file and close it, so it can be read back to compute the debuglink checksum.
726801 const path = options.extract_to.?;
......@@ -733,7 +808,14 @@ fn stripElf(
733808 }
734809
735810 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 });
811 try elf_file.emit(allocator, out_file, in_file, .{
812 .section_filter = filter,
813 .debuglink = debuglink,
814 .compress_debug = options.compress_debug,
815 .add_section = options.add_section,
816 .set_section_alignment = options.set_section_alignment,
817 .set_section_flags = options.set_section_flags,
818 });
737819 },
738820 }
739821}
......@@ -786,7 +868,7 @@ fn ElfFile(comptime is_64: bool) type {
786868 // program header: list of segments
787869 const program_segments = blk: {
788870 if (@sizeOf(Elf_Phdr) != header.phentsize)
789 fatal("zig objcopy: unsuported ELF file, unexpected phentsize ({d})", .{header.phentsize});
871 fatal("zig objcopy: unsupported ELF file, unexpected phentsize ({d})", .{header.phentsize});
790872
791873 const program_header = try allocator.alloc(Elf_Phdr, header.phnum);
792874 const bytes_read = try in_file.preadAll(std.mem.sliceAsBytes(program_header), header.phoff);
......@@ -798,7 +880,7 @@ fn ElfFile(comptime is_64: bool) type {
798880 // section header
799881 const sections = blk: {
800882 if (@sizeOf(Elf_Shdr) != header.shentsize)
801 fatal("zig objcopy: unsuported ELF file, unexpected shentsize ({d})", .{header.shentsize});
883 fatal("zig objcopy: unsupported ELF file, unexpected shentsize ({d})", .{header.shentsize});
802884
803885 const section_header = try allocator.alloc(Section, header.shnum);
804886
......@@ -896,6 +978,9 @@ fn ElfFile(comptime is_64: bool) type {
896978 section_filter: Filter = .all,
897979 debuglink: ?DebugLink = null,
898980 compress_debug: bool = false,
981 add_section: ?AddSection = null,
982 set_section_alignment: ?SetSectionAlignment = null,
983 set_section_flags: ?SetSectionFlags = null,
899984 };
900985 fn emit(self: *const Self, gpa: Allocator, out_file: File, in_file: File, options: EmitElfOptions) !void {
901986 var arena = std.heap.ArenaAllocator.init(gpa);
......@@ -934,6 +1019,10 @@ fn ElfFile(comptime is_64: bool) type {
9341019 if (options.debuglink != null)
9351020 next_idx += 1;
9361021
1022 if (options.add_section != null) {
1023 next_idx += 1;
1024 }
1025
9371026 break :blk next_idx;
9381027 };
9391028
......@@ -959,6 +1048,28 @@ fn ElfFile(comptime is_64: bool) type {
9591048 break :blk new_offset;
9601049 };
9611050
1051 // add user section to the string table if needed
1052 const user_section_name: u32 = blk: {
1053 if (options.add_section == null) break :blk elf.SHN_UNDEF;
1054 if (self.raw_elf_header.e_shstrndx == elf.SHN_UNDEF)
1055 fatal("zig objcopy: no strtab, cannot add the user section", .{}); // TODO add the section if needed?
1056
1057 const strtab = &self.sections[self.raw_elf_header.e_shstrndx];
1058 const update = &sections_update[self.raw_elf_header.e_shstrndx];
1059
1060 const name = options.add_section.?.section_name;
1061 const new_offset: u32 = @intCast(strtab.payload.?.len);
1062 const buf = try allocator.alignedAlloc(u8, section_memory_align, new_offset + name.len + 1);
1063 @memcpy(buf[0..new_offset], strtab.payload.?);
1064 @memcpy(buf[new_offset..][0..name.len], name);
1065 buf[new_offset + name.len] = 0;
1066
1067 assert(update.action == .keep);
1068 update.payload = buf;
1069
1070 break :blk new_offset;
1071 };
1072
9621073 // maybe compress .debug sections
9631074 if (options.compress_debug) {
9641075 for (self.sections[1..], sections_update[1..]) |section, *update| {
......@@ -1018,7 +1129,7 @@ fn ElfFile(comptime is_64: bool) type {
10181129 if (section.section.sh_type == elf.SHT_NOBITS)
10191130 continue;
10201131 if (section.section.sh_offset < offset) {
1021 fatal("zig objcopy: unsuported ELF file", .{});
1132 fatal("zig objcopy: unsupported ELF file", .{});
10221133 }
10231134 offset = section.section.sh_offset;
10241135 }
......@@ -1134,10 +1245,100 @@ fn ElfFile(comptime is_64: bool) type {
11341245 eof_offset += @as(Elf_OffSize, @intCast(payload.len));
11351246 }
11361247
1248 // --add-section
1249 if (options.add_section) |add_section| {
1250 var section_file = fs.cwd().openFile(add_section.file_path, .{}) catch |err|
1251 fatal("unable to open '{s}': {s}", .{ add_section.file_path, @errorName(err) });
1252 defer section_file.close();
1253
1254 const payload = try section_file.readToEndAlloc(arena.allocator(), std.math.maxInt(usize));
1255
1256 dest_sections[dest_section_idx] = Elf_Shdr{
1257 .sh_name = user_section_name,
1258 .sh_type = elf.SHT_PROGBITS,
1259 .sh_flags = 0,
1260 .sh_addr = 0,
1261 .sh_offset = eof_offset,
1262 .sh_size = @intCast(payload.len),
1263 .sh_link = elf.SHN_UNDEF,
1264 .sh_info = elf.SHN_UNDEF,
1265 .sh_addralign = 4,
1266 .sh_entsize = 0,
1267 };
1268 dest_section_idx += 1;
1269
1270 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = payload, .out_offset = eof_offset } });
1271 eof_offset += @as(Elf_OffSize, @intCast(payload.len));
1272 }
1273
11371274 assert(dest_section_idx == new_shnum);
11381275 break :blk dest_sections;
11391276 };
11401277
1278 // --set-section-alignment: overwrite alignment
1279 if (options.set_section_alignment) |set_align| {
1280 if (self.raw_elf_header.e_shstrndx == elf.SHN_UNDEF)
1281 fatal("zig objcopy: no strtab, cannot add the user section", .{}); // TODO add the section if needed?
1282
1283 const strtab = &sections_update[self.raw_elf_header.e_shstrndx];
1284 for (updated_section_header) |*section| {
1285 const section_name = std.mem.span(@as([*:0]const u8, @ptrCast(&strtab.payload.?[section.sh_name])));
1286 if (std.mem.eql(u8, section_name, set_align.section_name)) {
1287 section.sh_addralign = set_align.alignment;
1288 break;
1289 }
1290 } else std.log.warn("Skipping --set-section-alignment. Section '{s}' not found", .{set_align.section_name});
1291 }
1292
1293 // --set-section-flags: overwrite flags
1294 if (options.set_section_flags) |set_flags| {
1295 if (self.raw_elf_header.e_shstrndx == elf.SHN_UNDEF)
1296 fatal("zig objcopy: no strtab, cannot add the user section", .{}); // TODO add the section if needed?
1297
1298 const strtab = &sections_update[self.raw_elf_header.e_shstrndx];
1299 for (updated_section_header) |*section| {
1300 const section_name = std.mem.span(@as([*:0]const u8, @ptrCast(&strtab.payload.?[section.sh_name])));
1301 if (std.mem.eql(u8, section_name, set_flags.section_name)) {
1302 section.sh_flags = std.elf.SHF_WRITE; // default is writable cleared by "readonly"
1303 const f = set_flags.flags;
1304
1305 // Supporting a subset of GNU and LLVM objcopy for ELF only
1306 // GNU:
1307 // alloc: add SHF_ALLOC
1308 // contents: if section is SHT_NOBITS, set SHT_PROGBITS, otherwise do nothing
1309 // load: if section is SHT_NOBITS, set SHT_PROGBITS, otherwise do nothing (same as contents)
1310 // noload: not ELF relevant
1311 // readonly: clear default SHF_WRITE flag
1312 // code: add SHF_EXECINSTR
1313 // data: not ELF relevant
1314 // rom: ignored
1315 // exclude: add SHF_EXCLUDE
1316 // share: not ELF relevant
1317 // debug: not ELF relevant
1318 // large: add SHF_X86_64_LARGE. Fatal error if target is not x86_64
1319 if (f.alloc) section.sh_flags |= std.elf.SHF_ALLOC;
1320 if (f.contents or f.load) {
1321 if (section.sh_type == std.elf.SHT_NOBITS) section.sh_type = std.elf.SHT_PROGBITS;
1322 }
1323 if (f.readonly) section.sh_flags &= ~@as(@TypeOf(section.sh_type), std.elf.SHF_WRITE);
1324 if (f.code) section.sh_flags |= std.elf.SHF_EXECINSTR;
1325 if (f.exclude) section.sh_flags |= std.elf.SHF_EXCLUDE;
1326 if (f.large) {
1327 if (updated_elf_header.e_machine != std.elf.EM.X86_64)
1328 fatal("zig objcopy: 'large' section flag is only supported on x86_64 targets", .{});
1329 section.sh_flags |= std.elf.SHF_X86_64_LARGE;
1330 }
1331
1332 // LLVM:
1333 // merge: add SHF_MERGE
1334 // strings: add SHF_STRINGS
1335 if (f.merge) section.sh_flags |= std.elf.SHF_MERGE;
1336 if (f.strings) section.sh_flags |= std.elf.SHF_STRINGS;
1337 break;
1338 }
1339 } else std.log.warn("Skipping --set-section-flags. Section '{s}' not found", .{set_flags.section_name});
1340 }
1341
11411342 // write the section header at the tail
11421343 {
11431344 const offset = std.mem.alignForward(Elf_OffSize, eof_offset, @alignOf(Elf_Shdr));
......@@ -1361,3 +1562,111 @@ const ElfFileHelper = struct {
13611562 return hasher.final();
13621563 }
13631564};
1565
1566const SectionFlags = packed struct {
1567 alloc: bool = false,
1568 contents: bool = false,
1569 load: bool = false,
1570 noload: bool = false,
1571 readonly: bool = false,
1572 code: bool = false,
1573 data: bool = false,
1574 rom: bool = false,
1575 exclude: bool = false,
1576 shared: bool = false,
1577 debug: bool = false,
1578 large: bool = false,
1579 merge: bool = false,
1580 strings: bool = false,
1581};
1582
1583fn parseSectionFlags(comma_separated_flags: []const u8) SectionFlags {
1584 const P = struct {
1585 fn parse(flags: *SectionFlags, string: []const u8) void {
1586 if (string.len == 0) return;
1587
1588 if (std.mem.eql(u8, string, "alloc")) {
1589 flags.alloc = true;
1590 } else if (std.mem.eql(u8, string, "contents")) {
1591 flags.contents = true;
1592 } else if (std.mem.eql(u8, string, "load")) {
1593 flags.load = true;
1594 } else if (std.mem.eql(u8, string, "noload")) {
1595 flags.noload = true;
1596 } else if (std.mem.eql(u8, string, "readonly")) {
1597 flags.readonly = true;
1598 } else if (std.mem.eql(u8, string, "code")) {
1599 flags.code = true;
1600 } else if (std.mem.eql(u8, string, "data")) {
1601 flags.data = true;
1602 } else if (std.mem.eql(u8, string, "rom")) {
1603 flags.rom = true;
1604 } else if (std.mem.eql(u8, string, "exclude")) {
1605 flags.exclude = true;
1606 } else if (std.mem.eql(u8, string, "shared")) {
1607 flags.shared = true;
1608 } else if (std.mem.eql(u8, string, "debug")) {
1609 flags.debug = true;
1610 } else if (std.mem.eql(u8, string, "large")) {
1611 flags.large = true;
1612 } else if (std.mem.eql(u8, string, "merge")) {
1613 flags.merge = true;
1614 } else if (std.mem.eql(u8, string, "strings")) {
1615 flags.strings = true;
1616 } else {
1617 std.log.warn("Skipping unrecognized section flag '{s}'", .{string});
1618 }
1619 }
1620 };
1621
1622 var flags = SectionFlags{};
1623 var offset: usize = 0;
1624 for (comma_separated_flags, 0..) |c, i| {
1625 if (c == ',') {
1626 defer offset = i + 1;
1627 const string = comma_separated_flags[offset..i];
1628 P.parse(&flags, string);
1629 }
1630 }
1631 P.parse(&flags, comma_separated_flags[offset..]);
1632 return flags;
1633}
1634
1635test "Parse section flags" {
1636 const F = SectionFlags;
1637 try std.testing.expectEqual(F{}, parseSectionFlags(""));
1638 try std.testing.expectEqual(F{}, parseSectionFlags(","));
1639 try std.testing.expectEqual(F{}, parseSectionFlags("abc"));
1640 try std.testing.expectEqual(F{ .alloc = true }, parseSectionFlags("alloc"));
1641 try std.testing.expectEqual(F{ .data = true }, parseSectionFlags("data,"));
1642 try std.testing.expectEqual(F{ .alloc = true, .code = true }, parseSectionFlags("alloc,code"));
1643 try std.testing.expectEqual(F{ .alloc = true, .code = true }, parseSectionFlags("alloc,code,not_supported"));
1644}
1645
1646const SplitResult = struct { first: []const u8, second: []const u8 };
1647
1648fn splitOption(option: []const u8) ?SplitResult {
1649 const separator = '=';
1650 if (option.len < 3) return null; // minimum "a=b"
1651 for (1..option.len - 1) |i| {
1652 if (option[i] == separator) return .{
1653 .first = option[0..i],
1654 .second = option[i + 1 ..],
1655 };
1656 }
1657 return null;
1658}
1659
1660test "Split option" {
1661 {
1662 const split = splitOption(".abc=123");
1663 try std.testing.expect(split != null);
1664 try std.testing.expectEqualStrings(".abc", split.?.first);
1665 try std.testing.expectEqualStrings("123", split.?.second);
1666 }
1667
1668 try std.testing.expectEqual(null, splitOption(""));
1669 try std.testing.expectEqual(null, splitOption("=abc"));
1670 try std.testing.expectEqual(null, splitOption("abc="));
1671 try std.testing.expectEqual(null, splitOption("abc"));
1672}
lib/std/Build/Step/ObjCopy.zig+80
......@@ -26,6 +26,50 @@ pub const Strip = enum {
2626 debug_and_symbols,
2727};
2828
29pub const SectionFlags = packed struct {
30 /// add SHF_ALLOC
31 alloc: bool = false,
32
33 /// if section is SHT_NOBITS, set SHT_PROGBITS, otherwise do nothing
34 contents: bool = false,
35
36 /// if section is SHT_NOBITS, set SHT_PROGBITS, otherwise do nothing (same as contents)
37 load: bool = false,
38
39 /// readonly: clear default SHF_WRITE flag
40 readonly: bool = false,
41
42 /// add SHF_EXECINSTR
43 code: bool = false,
44
45 /// add SHF_EXCLUDE
46 exclude: bool = false,
47
48 /// add SHF_X86_64_LARGE. Fatal error if target is not x86_64
49 large: bool = false,
50
51 /// add SHF_MERGE
52 merge: bool = false,
53
54 /// add SHF_STRINGS
55 strings: bool = false,
56};
57
58pub const AddSection = struct {
59 section_name: []const u8,
60 file_path: std.Build.LazyPath,
61};
62
63pub const SetSectionAlignment = struct {
64 section_name: []const u8,
65 alignment: u32,
66};
67
68pub const SetSectionFlags = struct {
69 section_name: []const u8,
70 flags: SectionFlags,
71};
72
2973step: Step,
3074input_file: std.Build.LazyPath,
3175basename: []const u8,
......@@ -38,6 +82,10 @@ pad_to: ?u64,
3882strip: Strip,
3983compress_debug: bool,
4084
85add_section: ?AddSection,
86set_section_alignment: ?SetSectionAlignment,
87set_section_flags: ?SetSectionFlags,
88
4189pub const Options = struct {
4290 basename: ?[]const u8 = null,
4391 format: ?RawFormat = null,
......@@ -51,6 +99,10 @@ pub const Options = struct {
5199 /// note: the `basename` is baked into the elf file to specify the link to the separate debug file.
52100 /// see https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
53101 extract_to_separate_file: bool = false,
102
103 add_section: ?AddSection = null,
104 set_section_alignment: ?SetSectionAlignment = null,
105 set_section_flags: ?SetSectionFlags = null,
54106};
55107
56108pub fn create(
......@@ -75,6 +127,9 @@ pub fn create(
75127 .pad_to = options.pad_to,
76128 .strip = options.strip,
77129 .compress_debug = options.compress_debug,
130 .add_section = options.add_section,
131 .set_section_alignment = options.set_section_alignment,
132 .set_section_flags = options.set_section_flags,
78133 };
79134 input_file.addStepDependencies(&objcopy.step);
80135 return objcopy;
......@@ -155,6 +210,31 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
155210 if (objcopy.output_file_debug != null) {
156211 try argv.appendSlice(&.{b.fmt("--extract-to={s}", .{full_dest_path_debug})});
157212 }
213 if (objcopy.add_section) |section| {
214 try argv.append("--add-section");
215 try argv.appendSlice(&.{b.fmt("{s}={s}", .{ section.section_name, section.file_path.getPath(b) })});
216 }
217 if (objcopy.set_section_alignment) |set_align| {
218 try argv.append("--set-section-alignment");
219 try argv.appendSlice(&.{b.fmt("{s}={d}", .{ set_align.section_name, set_align.alignment })});
220 }
221 if (objcopy.set_section_flags) |set_flags| {
222 const f = set_flags.flags;
223 // trailing comma is allowed
224 try argv.append("--set-section-flags");
225 try argv.appendSlice(&.{b.fmt("{s}={s}{s}{s}{s}{s}{s}{s}{s}{s}", .{
226 set_flags.section_name,
227 if (f.alloc) "alloc," else "",
228 if (f.contents) "contents," else "",
229 if (f.load) "load," else "",
230 if (f.readonly) "readonly," else "",
231 if (f.code) "code," else "",
232 if (f.exclude) "exclude," else "",
233 if (f.large) "large," else "",
234 if (f.merge) "merge," else "",
235 if (f.strings) "strings," else "",
236 })});
237 }
158238
159239 try argv.appendSlice(&.{ full_src_path, full_dest_path });
160240