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(...@@ -40,6 +40,9 @@ 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: ?AddSection = null;
44 var set_section_alignment: ?SetSectionAlignment = null;
45 var set_section_flags: ?SetSectionFlags = null;
43 while (i < args.len) : (i += 1) {46 while (i < args.len) : (i += 1) {
44 const arg = args[i];47 const arg = args[i];
45 if (!mem.startsWith(u8, arg, "-")) {48 if (!mem.startsWith(u8, arg, "-")) {
...@@ -104,6 +107,37 @@ fn cmdObjCopy(...@@ -104,6 +107,37 @@ fn cmdObjCopy(
104 i += 1;107 i += 1;
105 if (i >= args.len) fatal("expected another argument after '{s}'", .{arg});108 if (i >= args.len) fatal("expected another argument after '{s}'", .{arg});
106 opt_extract = args[i];109 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 }
107 } else {141 } else {
108 fatal("unrecognized argument: '{s}'", .{arg});142 fatal("unrecognized argument: '{s}'", .{arg});
109 }143 }
...@@ -151,6 +185,12 @@ fn cmdObjCopy(...@@ -151,6 +185,12 @@ fn cmdObjCopy(
151 fatal("zig objcopy: ELF to RAW or HEX copying does not support --strip", .{});185 fatal("zig objcopy: ELF to RAW or HEX copying does not support --strip", .{});
152 if (opt_extract != null)186 if (opt_extract != null)
153 fatal("zig objcopy: ELF to RAW or HEX copying does not support --extract-to", .{});187 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
155 try emitElf(arena, in_file, out_file, elf_hdr, .{195 try emitElf(arena, in_file, out_file, elf_hdr, .{
156 .ofmt = out_fmt,196 .ofmt = out_fmt,
...@@ -175,6 +215,9 @@ fn cmdObjCopy(...@@ -175,6 +215,9 @@ fn cmdObjCopy(
175 .add_debuglink = opt_add_debuglink,215 .add_debuglink = opt_add_debuglink,
176 .extract_to = opt_extract,216 .extract_to = opt_extract,
177 .compress_debug = compress_debug_sections,217 .compress_debug = compress_debug_sections,
218 .add_section = add_section,
219 .set_section_alignment = set_section_alignment,
220 .set_section_flags = set_section_flags,
178 });221 });
179 return std.process.cleanExit();222 return std.process.cleanExit();
180 },223 },
...@@ -217,18 +260,21 @@ const usage =...@@ -217,18 +260,21 @@ const usage =
217 \\Usage: zig objcopy [options] input output260 \\Usage: zig objcopy [options] input output
218 \\261 \\
219 \\Options:262 \\Options:
220 \\ -h, --help Print this help and exit263 \\ -h, --help Print this help and exit
221 \\ --output-target=<value> Format of the output file264 \\ --output-target=<value> Format of the output file
222 \\ -O <value> Alias for --output-target265 \\ -O <value> Alias for --output-target
223 \\ --only-section=<section> Remove all but <section>266 \\ --only-section=<section> Remove all but <section>
224 \\ -j <value> Alias for --only-section267 \\ -j <value> Alias for --only-section
225 \\ --pad-to <addr> Pad the last section up to address <addr>268 \\ --pad-to <addr> Pad the last section up to address <addr>
226 \\ --strip-debug, -g Remove all debug sections from the output.269 \\ --strip-debug, -g Remove all debug sections from the output.
227 \\ --strip-all, -S Remove all debug sections and symbol table from the output.270 \\ --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.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.
229 \\ --add-gnu-debuglink=<file> Creates a .gnu_debuglink section which contains a reference to <file> and adds it to the output file.272 \\ --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.273 \\ --extract-to <file> Extract the removed sections into <file>, and add a .gnu-debuglink section.
231 \\ --compress-debug-sections Compress DWARF debug sections with zlib274 \\ --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>.
232 \\278 \\
233;279;
234280
...@@ -236,6 +282,24 @@ pub const EmitRawElfOptions = struct {...@@ -236,6 +282,24 @@ pub const EmitRawElfOptions = struct {
236 ofmt: std.Target.ObjectFormat,282 ofmt: std.Target.ObjectFormat,
237 only_section: ?[]const u8 = null,283 only_section: ?[]const u8 = null,
238 pad_to: ?u64 = null,284 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,
239};303};
240304
241fn emitElf(305fn emitElf(
...@@ -678,6 +742,9 @@ const StripElfOptions = struct {...@@ -678,6 +742,9 @@ const StripElfOptions = struct {
678 strip_debug: bool = false,742 strip_debug: bool = false,
679 only_keep_debug: bool = false,743 only_keep_debug: bool = false,
680 compress_debug: bool = false,744 compress_debug: bool = false,
745 add_section: ?AddSection,
746 set_section_alignment: ?SetSectionAlignment,
747 set_section_flags: ?SetSectionFlags,
681};748};
682749
683fn stripElf(750fn stripElf(
...@@ -721,6 +788,14 @@ fn stripElf(...@@ -721,6 +788,14 @@ fn stripElf(
721 var elf_file = try ElfFile(is_64).parse(allocator, in_file, elf_hdr);788 var elf_file = try ElfFile(is_64).parse(allocator, in_file, elf_hdr);
722 defer elf_file.deinit();789 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
724 if (filter_complement) |flt| {799 if (filter_complement) |flt| {
725 // write the .dbg file and close it, so it can be read back to compute the debuglink checksum.800 // write the .dbg file and close it, so it can be read back to compute the debuglink checksum.
726 const path = options.extract_to.?;801 const path = options.extract_to.?;
...@@ -733,7 +808,14 @@ fn stripElf(...@@ -733,7 +808,14 @@ fn stripElf(
733 }808 }
734809
735 const debuglink: ?DebugLink = if (debuglink_path) |path| ElfFileHelper.createDebugLink(path) else null;810 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 });
737 },819 },
738 }820 }
739}821}
...@@ -786,7 +868,7 @@ fn ElfFile(comptime is_64: bool) type {...@@ -786,7 +868,7 @@ fn ElfFile(comptime is_64: bool) type {
786 // program header: list of segments868 // program header: list of segments
787 const program_segments = blk: {869 const program_segments = blk: {
788 if (@sizeOf(Elf_Phdr) != header.phentsize)870 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
791 const program_header = try allocator.alloc(Elf_Phdr, header.phnum);873 const program_header = try allocator.alloc(Elf_Phdr, header.phnum);
792 const bytes_read = try in_file.preadAll(std.mem.sliceAsBytes(program_header), header.phoff);874 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 {...@@ -798,7 +880,7 @@ fn ElfFile(comptime is_64: bool) type {
798 // section header880 // section header
799 const sections = blk: {881 const sections = blk: {
800 if (@sizeOf(Elf_Shdr) != header.shentsize)882 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
803 const section_header = try allocator.alloc(Section, header.shnum);885 const section_header = try allocator.alloc(Section, header.shnum);
804886
...@@ -896,6 +978,9 @@ fn ElfFile(comptime is_64: bool) type {...@@ -896,6 +978,9 @@ fn ElfFile(comptime is_64: bool) type {
896 section_filter: Filter = .all,978 section_filter: Filter = .all,
897 debuglink: ?DebugLink = null,979 debuglink: ?DebugLink = null,
898 compress_debug: bool = false,980 compress_debug: bool = false,
981 add_section: ?AddSection = null,
982 set_section_alignment: ?SetSectionAlignment = null,
983 set_section_flags: ?SetSectionFlags = null,
899 };984 };
900 fn emit(self: *const Self, gpa: Allocator, out_file: File, in_file: File, options: EmitElfOptions) !void {985 fn emit(self: *const Self, gpa: Allocator, out_file: File, in_file: File, options: EmitElfOptions) !void {
901 var arena = std.heap.ArenaAllocator.init(gpa);986 var arena = std.heap.ArenaAllocator.init(gpa);
...@@ -934,6 +1019,10 @@ fn ElfFile(comptime is_64: bool) type {...@@ -934,6 +1019,10 @@ fn ElfFile(comptime is_64: bool) type {
934 if (options.debuglink != null)1019 if (options.debuglink != null)
935 next_idx += 1;1020 next_idx += 1;
9361021
1022 if (options.add_section != null) {
1023 next_idx += 1;
1024 }
1025
937 break :blk next_idx;1026 break :blk next_idx;
938 };1027 };
9391028
...@@ -959,6 +1048,28 @@ fn ElfFile(comptime is_64: bool) type {...@@ -959,6 +1048,28 @@ fn ElfFile(comptime is_64: bool) type {
959 break :blk new_offset;1048 break :blk new_offset;
960 };1049 };
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
962 // maybe compress .debug sections1073 // maybe compress .debug sections
963 if (options.compress_debug) {1074 if (options.compress_debug) {
964 for (self.sections[1..], sections_update[1..]) |section, *update| {1075 for (self.sections[1..], sections_update[1..]) |section, *update| {
...@@ -1018,7 +1129,7 @@ fn ElfFile(comptime is_64: bool) type {...@@ -1018,7 +1129,7 @@ fn ElfFile(comptime is_64: bool) type {
1018 if (section.section.sh_type == elf.SHT_NOBITS)1129 if (section.section.sh_type == elf.SHT_NOBITS)
1019 continue;1130 continue;
1020 if (section.section.sh_offset < offset) {1131 if (section.section.sh_offset < offset) {
1021 fatal("zig objcopy: unsuported ELF file", .{});1132 fatal("zig objcopy: unsupported ELF file", .{});
1022 }1133 }
1023 offset = section.section.sh_offset;1134 offset = section.section.sh_offset;
1024 }1135 }
...@@ -1134,10 +1245,100 @@ fn ElfFile(comptime is_64: bool) type {...@@ -1134,10 +1245,100 @@ fn ElfFile(comptime is_64: bool) type {
1134 eof_offset += @as(Elf_OffSize, @intCast(payload.len));1245 eof_offset += @as(Elf_OffSize, @intCast(payload.len));
1135 }1246 }
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
1137 assert(dest_section_idx == new_shnum);1274 assert(dest_section_idx == new_shnum);
1138 break :blk dest_sections;1275 break :blk dest_sections;
1139 };1276 };
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
1141 // write the section header at the tail1342 // write the section header at the tail
1142 {1343 {
1143 const offset = std.mem.alignForward(Elf_OffSize, eof_offset, @alignOf(Elf_Shdr));1344 const offset = std.mem.alignForward(Elf_OffSize, eof_offset, @alignOf(Elf_Shdr));
...@@ -1361,3 +1562,111 @@ const ElfFileHelper = struct {...@@ -1361,3 +1562,111 @@ const ElfFileHelper = struct {
1361 return hasher.final();1562 return hasher.final();
1362 }1563 }
1363};1564};
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 {...@@ -26,6 +26,50 @@ pub const Strip = enum {
26 debug_and_symbols,26 debug_and_symbols,
27};27};
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
29step: Step,73step: Step,
30input_file: std.Build.LazyPath,74input_file: std.Build.LazyPath,
31basename: []const u8,75basename: []const u8,
...@@ -38,6 +82,10 @@ pad_to: ?u64,...@@ -38,6 +82,10 @@ pad_to: ?u64,
38strip: Strip,82strip: Strip,
39compress_debug: bool,83compress_debug: bool,
4084
85add_section: ?AddSection,
86set_section_alignment: ?SetSectionAlignment,
87set_section_flags: ?SetSectionFlags,
88
41pub const Options = struct {89pub const Options = struct {
42 basename: ?[]const u8 = null,90 basename: ?[]const u8 = null,
43 format: ?RawFormat = null,91 format: ?RawFormat = null,
...@@ -51,6 +99,10 @@ pub const Options = struct {...@@ -51,6 +99,10 @@ pub const Options = struct {
51 /// note: the `basename` is baked into the elf file to specify the link to the separate debug file.99 /// note: the `basename` is baked into the elf file to specify the link to the separate debug file.
52 /// see https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html100 /// see https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
53 extract_to_separate_file: bool = false,101 extract_to_separate_file: bool = false,
102
103 add_section: ?AddSection = null,
104 set_section_alignment: ?SetSectionAlignment = null,
105 set_section_flags: ?SetSectionFlags = null,
54};106};
55107
56pub fn create(108pub fn create(
...@@ -75,6 +127,9 @@ pub fn create(...@@ -75,6 +127,9 @@ pub fn create(
75 .pad_to = options.pad_to,127 .pad_to = options.pad_to,
76 .strip = options.strip,128 .strip = options.strip,
77 .compress_debug = options.compress_debug,129 .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,
78 };133 };
79 input_file.addStepDependencies(&objcopy.step);134 input_file.addStepDependencies(&objcopy.step);
80 return objcopy;135 return objcopy;
...@@ -155,6 +210,31 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -155,6 +210,31 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
155 if (objcopy.output_file_debug != null) {210 if (objcopy.output_file_debug != null) {
156 try argv.appendSlice(&.{b.fmt("--extract-to={s}", .{full_dest_path_debug})});211 try argv.appendSlice(&.{b.fmt("--extract-to={s}", .{full_dest_path_debug})});
157 }212 }
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
159 try argv.appendSlice(&.{ full_src_path, full_dest_path });239 try argv.appendSlice(&.{ full_src_path, full_dest_path });
160240