diff --git a/lib/std/Build/Step/ObjCopy.zig b/lib/std/Build/Step/ObjCopy.zig index 1fc6aa9a41..6691107bf5 100644 --- a/lib/std/Build/Step/ObjCopy.zig +++ b/lib/std/Build/Step/ObjCopy.zig @@ -17,22 +17,40 @@ pub const base_id: Step.Id = .objcopy; pub const RawFormat = enum { bin, hex, + elf, +}; + +pub const Strip = enum { + none, + debug, + debug_and_symbols, }; step: Step, input_file: std.Build.LazyPath, basename: []const u8, output_file: std.Build.GeneratedFile, +output_file_debug: ?std.Build.GeneratedFile, format: ?RawFormat, only_section: ?[]const u8, pad_to: ?u64, +strip: Strip, +compress_debug: bool, pub const Options = struct { basename: ?[]const u8 = null, format: ?RawFormat = null, only_section: ?[]const u8 = null, pad_to: ?u64 = null, + + compress_debug: bool = false, + strip: Strip = .none, + + /// Put the stripped out debug sections in a separate file. + /// note: the `basename` is baked into the elf file to specify the link to the separate debug file. + /// see https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html + extract_to_separate_file: bool = false, }; pub fn create( @@ -51,10 +69,12 @@ pub fn create( .input_file = input_file, .basename = options.basename orelse input_file.getDisplayName(), .output_file = std.Build.GeneratedFile{ .step = &self.step }, - + .output_file_debug = if (options.strip != .none and options.extract_to_separate_file) std.Build.GeneratedFile{ .step = &self.step } else null, .format = options.format, .only_section = options.only_section, .pad_to = options.pad_to, + .strip = options.strip, + .compress_debug = options.compress_debug, }; input_file.addStepDependencies(&self.step); return self; @@ -66,6 +86,9 @@ pub const getOutputSource = getOutput; pub fn getOutput(self: *const ObjCopy) std.Build.LazyPath { return .{ .generated = &self.output_file }; } +pub fn getOutputSeparatedDebug(self: *const ObjCopy) ?std.Build.LazyPath { + return if (self.output_file_debug) |*file| .{ .generated = file } else null; +} fn make(step: *Step, prog_node: *std.Progress.Node) !void { const b = step.owner; @@ -83,6 +106,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { man.hash.addOptionalBytes(self.only_section); man.hash.addOptional(self.pad_to); man.hash.addOptional(self.format); + man.hash.add(self.compress_debug); + man.hash.add(self.strip); + man.hash.add(self.output_file_debug != null); if (try step.cacheHit(&man)) { // Cache hit, skip subprocess execution. @@ -90,12 +116,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { self.output_file.path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename, }); + if (self.output_file_debug) |*file| { + file.path = try b.cache_root.join(b.allocator, &.{ + "o", &digest, b.fmt("{s}.debug", .{self.basename}), + }); + } return; } const digest = man.final(); - const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename }); const cache_path = "o" ++ fs.path.sep_str ++ digest; + const full_dest_path = try b.cache_root.join(b.allocator, &.{ cache_path, self.basename }); + const full_dest_path_debug = try b.cache_root.join(b.allocator, &.{ cache_path, b.fmt("{s}.debug", .{self.basename}) }); b.cache_root.handle.makePath(cache_path) catch |err| { return step.fail("unable to make path {s}: {s}", .{ cache_path, @errorName(err) }); }; @@ -106,13 +138,25 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (self.only_section) |only_section| { try argv.appendSlice(&.{ "-j", only_section }); } + switch (self.strip) { + .none => {}, + .debug => try argv.appendSlice(&.{"--strip-debug"}), + .debug_and_symbols => try argv.appendSlice(&.{"--strip-all"}), + } if (self.pad_to) |pad_to| { try argv.appendSlice(&.{ "--pad-to", b.fmt("{d}", .{pad_to}) }); } if (self.format) |format| switch (format) { .bin => try argv.appendSlice(&.{ "-O", "binary" }), .hex => try argv.appendSlice(&.{ "-O", "hex" }), + .elf => try argv.appendSlice(&.{ "-O", "elf" }), }; + if (self.compress_debug) { + try argv.appendSlice(&.{"--compress-debug-sections"}); + } + if (self.output_file_debug != null) { + try argv.appendSlice(&.{b.fmt("--extract-to={s}", .{full_dest_path_debug})}); + } try argv.appendSlice(&.{ full_src_path, full_dest_path }); @@ -120,5 +164,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = try step.evalZigProcess(argv.items, prog_node); self.output_file.path = full_dest_path; + if (self.output_file_debug) |*file| file.path = full_dest_path_debug; try man.writeManifest(); } diff --git a/src/objcopy.zig b/src/objcopy.zig index 887396ee18..ad405b27f7 100644 --- a/src/objcopy.zig +++ b/src/objcopy.zig @@ -104,9 +104,6 @@ pub fn cmdObjCopy( fatal("unable to open '{s}': {s}", .{ input, @errorName(err) }); defer in_file.close(); - var out_file = try fs.cwd().createFile(output, .{}); - defer out_file.close(); - const elf_hdr = std.elf.Header.read(in_file) catch |err| switch (err) { error.InvalidElfMagic => fatal("not an ELF file: '{s}'", .{input}), else => fatal("unable to read '{s}': {s}", .{ input, @errorName(err) }), @@ -126,6 +123,17 @@ pub fn cmdObjCopy( } }; + const mode = mode: { + if (out_fmt != .elf or !only_keep_debug) + break :mode fs.File.default_mode; + if (in_file.stat()) |stat| + break :mode stat.mode + else |_| + break :mode fs.File.default_mode; + }; + var out_file = try fs.cwd().createFile(output, .{ .mode = mode }); + defer out_file.close(); + switch (out_fmt) { .hex, .raw => { if (strip_debug or strip_all or only_keep_debug) @@ -345,7 +353,7 @@ const BinaryElfOutput = struct { const shstrtab_shdr = (try section_headers.next()).?; - const buffer = try allocator.alloc(u8, @as(usize, @intCast(shstrtab_shdr.sh_size))); + const buffer = try allocator.alloc(u8, @intCast(shstrtab_shdr.sh_size)); errdefer allocator.free(buffer); const num_read = try elf_file.preadAll(buffer, shstrtab_shdr.sh_offset); @@ -363,7 +371,7 @@ const BinaryElfOutput = struct { newSection.binaryOffset = 0; newSection.elfOffset = section.sh_offset; - newSection.fileSize = @as(usize, @intCast(section.sh_size)); + newSection.fileSize = @intCast(section.sh_size); newSection.segment = null; newSection.name = if (self.shstrtab) |shstrtab| @@ -382,7 +390,7 @@ const BinaryElfOutput = struct { newSegment.physicalAddress = if (phdr.p_paddr != 0) phdr.p_paddr else phdr.p_vaddr; newSegment.virtualAddress = phdr.p_vaddr; - newSegment.fileSize = @as(usize, @intCast(phdr.p_filesz)); + newSegment.fileSize = @intCast(phdr.p_filesz); newSegment.elfOffset = phdr.p_offset; newSegment.binaryOffset = 0; newSegment.firstSection = null; @@ -478,8 +486,8 @@ const HexWriter = struct { const MAX_PAYLOAD_LEN: u8 = 16; fn addressParts(address: u16) [2]u8 { - const msb = @as(u8, @truncate(address >> 8)); - const lsb = @as(u8, @truncate(address)); + const msb: u8 = @truncate(address >> 8); + const lsb: u8 = @truncate(address); return [2]u8{ msb, lsb }; } @@ -508,14 +516,14 @@ const HexWriter = struct { fn Data(address: u32, data: []const u8) Record { return Record{ - .address = @as(u16, @intCast(address % 0x10000)), + .address = @intCast(address % 0x10000), .payload = .{ .Data = data }, }; } fn Address(address: u32) Record { assert(address > 0xFFFF); - const segment = @as(u16, @intCast(address / 0x10000)); + const segment: u16 = @intCast(address / 0x10000); if (address > 0xFFFFF) { return Record{ .address = 0, @@ -540,7 +548,7 @@ const HexWriter = struct { fn checksum(self: Record) u8 { const payload_bytes = self.getPayloadBytes(); - var sum: u8 = @as(u8, @intCast(payload_bytes.len)); + var sum: u8 = @intCast(payload_bytes.len); const parts = addressParts(self.address); sum +%= parts[0]; sum +%= parts[1]; @@ -574,10 +582,10 @@ const HexWriter = struct { var buf: [MAX_PAYLOAD_LEN]u8 = undefined; var bytes_read: usize = 0; while (bytes_read < segment.fileSize) { - const row_address = @as(u32, @intCast(segment.physicalAddress + bytes_read)); + const row_address: u32 = @intCast(segment.physicalAddress + bytes_read); const remaining = segment.fileSize - bytes_read; - const to_read = @as(usize, @intCast(@min(remaining, MAX_PAYLOAD_LEN))); + const to_read: usize = @intCast(@min(remaining, MAX_PAYLOAD_LEN)); const did_read = try elf_file.preadAll(buf[0..to_read], segment.elfOffset + bytes_read); if (did_read < to_read) return error.UnexpectedEOF; @@ -593,7 +601,7 @@ const HexWriter = struct { try Record.Address(address).write(self.out_file); } try record.write(self.out_file); - self.prev_addr = @as(u32, @intCast(record.address + data.len)); + self.prev_addr = @intCast(record.address + data.len); } fn writeEOF(self: HexWriter) File.WriteError!void { @@ -814,7 +822,7 @@ fn ElfFile(comptime is_64: bool) type { const need_strings = (idx == header.shstrndx); if (need_data or need_strings) { - const buffer = try allocator.alignedAlloc(u8, section_memory_align, @as(usize, @intCast(section.section.sh_size))); + const buffer = try allocator.alignedAlloc(u8, section_memory_align, @intCast(section.section.sh_size)); const bytes_read = try in_file.preadAll(buffer, section.section.sh_offset); if (bytes_read != section.section.sh_size) return error.TRUNCATED_ELF; section.payload = buffer; @@ -935,7 +943,7 @@ fn ElfFile(comptime is_64: bool) type { const update = §ions_update[self.raw_elf_header.e_shstrndx]; const name: []const u8 = ".gnu_debuglink"; - const new_offset = @as(u32, @intCast(strtab.payload.?.len)); + const new_offset: u32 = @intCast(strtab.payload.?.len); const buf = try allocator.alignedAlloc(u8, section_memory_align, new_offset + name.len + 1); @memcpy(buf[0..new_offset], strtab.payload.?); @memcpy(buf[new_offset..][0..name.len], name); @@ -965,7 +973,7 @@ fn ElfFile(comptime is_64: bool) type { update.payload = payload; update.section = section.section; update.section.?.sh_addralign = @alignOf(Elf_Chdr); - update.section.?.sh_size = @as(Elf_OffSize, @intCast(payload.len)); + update.section.?.sh_size = @intCast(payload.len); update.section.?.sh_flags |= elf.SHF_COMPRESSED; } } @@ -1032,7 +1040,7 @@ fn ElfFile(comptime is_64: bool) type { dest.sh_info = sections_update[src.sh_info].remap_idx; if (payload) |data| - dest.sh_size = @as(Elf_OffSize, @intCast(data.len)); + dest.sh_size = @intCast(data.len); const addralign = if (src.sh_addralign == 0 or dest.sh_type == elf.SHT_NOBITS) 1 else src.sh_addralign; dest.sh_offset = std.mem.alignForward(Elf_OffSize, eof_offset, addralign); @@ -1110,7 +1118,7 @@ fn ElfFile(comptime is_64: bool) type { .sh_flags = 0, .sh_addr = 0, .sh_offset = eof_offset, - .sh_size = @as(Elf_OffSize, @intCast(payload.len)), + .sh_size = @intCast(payload.len), .sh_link = elf.SHN_UNDEF, .sh_info = elf.SHN_UNDEF, .sh_addralign = 4, @@ -1232,7 +1240,7 @@ const ElfFileHelper = struct { fused_cmd = null; } if (data.out_offset > offset) { - consolidated.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0..@as(usize, @intCast(data.out_offset - offset))], .out_offset = offset } }); + consolidated.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0..@intCast(data.out_offset - offset)], .out_offset = offset } }); } consolidated.appendAssumeCapacity(cmd); offset = data.out_offset + data.data.len; @@ -1249,7 +1257,7 @@ const ElfFileHelper = struct { } else { consolidated.appendAssumeCapacity(prev); if (range.out_offset > offset) { - consolidated.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0..@as(usize, @intCast(range.out_offset - offset))], .out_offset = offset } }); + consolidated.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0..@intCast(range.out_offset - offset)], .out_offset = offset } }); } fused_cmd = cmd; } @@ -1286,7 +1294,7 @@ const ElfFileHelper = struct { var section_reader = std.io.limitedReader(in_file.reader(), size); // allocate as large as decompressed data. if the compression doesn't fit, keep the data uncompressed. - const compressed_data = try allocator.alignedAlloc(u8, 8, @as(usize, @intCast(size))); + const compressed_data = try allocator.alignedAlloc(u8, 8, @intCast(size)); var compressed_stream = std.io.fixedBufferStream(compressed_data); try compressed_stream.writer().writeAll(prefix); @@ -1317,7 +1325,7 @@ const ElfFileHelper = struct { }; } - const compressed_len = @as(usize, @intCast(compressed_stream.getPos() catch unreachable)); + const compressed_len: usize = @intCast(compressed_stream.getPos() catch unreachable); const data = allocator.realloc(compressed_data, compressed_len) catch compressed_data; return data[0..compressed_len]; }