| ... | @@ -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", .{}); |
| 154 | | 194 | |
| 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 output | 260 | \\Usage: zig objcopy [options] input output |
| 218 | \\ | 261 | \\ |
| 219 | \\Options: | 262 | \\Options: |
| 220 | \\ -h, --help Print this help and exit | 263 | \\ -h, --help Print this help and exit |
| 221 | \\ --output-target=<value> Format of the output file | 264 | \\ --output-target=<value> Format of the output file |
| 222 | \\ -O <value> Alias for --output-target | 265 | \\ -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-section | 267 | \\ -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 zlib | 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>. |
| 232 | \\ | 278 | \\ |
| 233 | ; | 279 | ; |
| 234 | | 280 | |
| ... | @@ -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 | |
| | 290 | const AddSection = struct { |
| | 291 | section_name: []const u8, |
| | 292 | file_path: []const u8, |
| | 293 | }; |
| | 294 | |
| | 295 | const SetSectionAlignment = struct { |
| | 296 | section_name: []const u8, |
| | 297 | alignment: u32, |
| | 298 | }; |
| | 299 | |
| | 300 | const SetSectionFlags = struct { |
| | 301 | section_name: []const u8, |
| | 302 | flags: SectionFlags, |
| 239 | }; | 303 | }; |
| 240 | | 304 | |
| 241 | fn emitElf( | 305 | fn 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 | }; |
| 682 | | 749 | |
| 683 | fn stripElf( | 750 | fn 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(); |
| 723 | | 790 | |
| | 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 | } |
| 734 | | 809 | |
| 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 segments | 868 | // 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}); |
| 790 | | 872 | |
| 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 header | 880 | // 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}); |
| 802 | | 884 | |
| 803 | const section_header = try allocator.alloc(Section, header.shnum); | 885 | const section_header = try allocator.alloc(Section, header.shnum); |
| 804 | | 886 | |
| ... | @@ -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; |
| 936 | | 1021 | |
| | 1022 | if (options.add_section != null) { |
| | 1023 | next_idx += 1; |
| | 1024 | } |
| | 1025 | |
| 937 | break :blk next_idx; | 1026 | break :blk next_idx; |
| 938 | }; | 1027 | }; |
| 939 | | 1028 | |
| ... | @@ -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 | }; |
| 961 | | 1050 | |
| | 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 sections | 1073 | // 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 | } |
| 1136 | | 1247 | |
| | 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 | }; |
| 1140 | | 1277 | |
| | 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 tail | 1342 | // 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 | |
| | 1566 | const 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 | |
| | 1583 | fn 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 | |
| | 1635 | test "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 | |
| | 1646 | const SplitResult = struct { first: []const u8, second: []const u8 }; |
| | 1647 | |
| | 1648 | fn 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 | |
| | 1660 | test "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 | } |