| author | |
| committer | |
| log | 9be5323e93123a3979037997fa40a95b4c985b85 |
| tree | 8e6af2db5efc74d21ac7faad5b77c726a7276583 |
| parent | 5d3adc568c7f0d4720bbd283337404c3cad86479 |
This commit moves the logic from `std.build.InstallRawStep` into `zig
objcopy`. The options here are limited, but we can add features as
needed.
closes #9261
New issues can be opened for specific objcopy flag support.4 files changed, 583 insertions(+), 453 deletions(-)
lib/std/build/InstallRawStep.zig+29-452| ... | ... | @@ -1,4 +1,8 @@ |
| 1 | //! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy` | |
| 2 | //! subcommand rather than containing an implementation directly. | |
| 3 | ||
| 1 | 4 | const std = @import("std"); |
| 5 | const InstallRawStep = @This(); | |
| 2 | 6 | |
| 3 | 7 | const Allocator = std.mem.Allocator; |
| 4 | 8 | const ArenaAllocator = std.heap.ArenaAllocator; |
| ... | ... | @@ -13,406 +17,6 @@ const fs = std.fs; |
| 13 | 17 | const io = std.io; |
| 14 | 18 | const sort = std.sort; |
| 15 | 19 | |
| 16 | const BinaryElfSection = struct { | |
| 17 | elfOffset: u64, | |
| 18 | binaryOffset: u64, | |
| 19 | fileSize: usize, | |
| 20 | name: ?[]const u8, | |
| 21 | segment: ?*BinaryElfSegment, | |
| 22 | }; | |
| 23 | ||
| 24 | const BinaryElfSegment = struct { | |
| 25 | physicalAddress: u64, | |
| 26 | virtualAddress: u64, | |
| 27 | elfOffset: u64, | |
| 28 | binaryOffset: u64, | |
| 29 | fileSize: u64, | |
| 30 | firstSection: ?*BinaryElfSection, | |
| 31 | }; | |
| 32 | ||
| 33 | const BinaryElfOutput = struct { | |
| 34 | segments: ArrayListUnmanaged(*BinaryElfSegment), | |
| 35 | sections: ArrayListUnmanaged(*BinaryElfSection), | |
| 36 | allocator: Allocator, | |
| 37 | shstrtab: ?[]const u8, | |
| 38 | ||
| 39 | const Self = @This(); | |
| 40 | ||
| 41 | pub fn deinit(self: *Self) void { | |
| 42 | if (self.shstrtab) |shstrtab| | |
| 43 | self.allocator.free(shstrtab); | |
| 44 | self.sections.deinit(self.allocator); | |
| 45 | self.segments.deinit(self.allocator); | |
| 46 | } | |
| 47 | ||
| 48 | pub fn parse(allocator: Allocator, elf_file: File) !Self { | |
| 49 | var self: Self = .{ | |
| 50 | .segments = .{}, | |
| 51 | .sections = .{}, | |
| 52 | .allocator = allocator, | |
| 53 | .shstrtab = null, | |
| 54 | }; | |
| 55 | errdefer self.sections.deinit(allocator); | |
| 56 | errdefer self.segments.deinit(allocator); | |
| 57 | ||
| 58 | const elf_hdr = try std.elf.Header.read(&elf_file); | |
| 59 | ||
| 60 | self.shstrtab = blk: { | |
| 61 | if (elf_hdr.shstrndx >= elf_hdr.shnum) break :blk null; | |
| 62 | ||
| 63 | var section_headers = elf_hdr.section_header_iterator(&elf_file); | |
| 64 | ||
| 65 | var section_counter: usize = 0; | |
| 66 | while (section_counter < elf_hdr.shstrndx) : (section_counter += 1) { | |
| 67 | _ = (try section_headers.next()).?; | |
| 68 | } | |
| 69 | ||
| 70 | const shstrtab_shdr = (try section_headers.next()).?; | |
| 71 | ||
| 72 | const buffer = try allocator.alloc(u8, @intCast(usize, shstrtab_shdr.sh_size)); | |
| 73 | errdefer allocator.free(buffer); | |
| 74 | ||
| 75 | const num_read = try elf_file.preadAll(buffer, shstrtab_shdr.sh_offset); | |
| 76 | if (num_read != buffer.len) return error.EndOfStream; | |
| 77 | ||
| 78 | break :blk buffer; | |
| 79 | }; | |
| 80 | ||
| 81 | errdefer if (self.shstrtab) |shstrtab| allocator.free(shstrtab); | |
| 82 | ||
| 83 | var section_headers = elf_hdr.section_header_iterator(&elf_file); | |
| 84 | while (try section_headers.next()) |section| { | |
| 85 | if (sectionValidForOutput(section)) { | |
| 86 | const newSection = try allocator.create(BinaryElfSection); | |
| 87 | ||
| 88 | newSection.binaryOffset = 0; | |
| 89 | newSection.elfOffset = section.sh_offset; | |
| 90 | newSection.fileSize = @intCast(usize, section.sh_size); | |
| 91 | newSection.segment = null; | |
| 92 | ||
| 93 | newSection.name = if (self.shstrtab) |shstrtab| | |
| 94 | std.mem.span(@ptrCast([*:0]const u8, &shstrtab[section.sh_name])) | |
| 95 | else | |
| 96 | null; | |
| 97 | ||
| 98 | try self.sections.append(allocator, newSection); | |
| 99 | } | |
| 100 | } | |
| 101 | ||
| 102 | var program_headers = elf_hdr.program_header_iterator(&elf_file); | |
| 103 | while (try program_headers.next()) |phdr| { | |
| 104 | if (phdr.p_type == elf.PT_LOAD) { | |
| 105 | const newSegment = try allocator.create(BinaryElfSegment); | |
| 106 | ||
| 107 | newSegment.physicalAddress = if (phdr.p_paddr != 0) phdr.p_paddr else phdr.p_vaddr; | |
| 108 | newSegment.virtualAddress = phdr.p_vaddr; | |
| 109 | newSegment.fileSize = @intCast(usize, phdr.p_filesz); | |
| 110 | newSegment.elfOffset = phdr.p_offset; | |
| 111 | newSegment.binaryOffset = 0; | |
| 112 | newSegment.firstSection = null; | |
| 113 | ||
| 114 | for (self.sections.items) |section| { | |
| 115 | if (sectionWithinSegment(section, phdr)) { | |
| 116 | if (section.segment) |sectionSegment| { | |
| 117 | if (sectionSegment.elfOffset > newSegment.elfOffset) { | |
| 118 | section.segment = newSegment; | |
| 119 | } | |
| 120 | } else { | |
| 121 | section.segment = newSegment; | |
| 122 | } | |
| 123 | ||
| 124 | if (newSegment.firstSection == null) { | |
| 125 | newSegment.firstSection = section; | |
| 126 | } | |
| 127 | } | |
| 128 | } | |
| 129 | ||
| 130 | try self.segments.append(allocator, newSegment); | |
| 131 | } | |
| 132 | } | |
| 133 | ||
| 134 | sort.sort(*BinaryElfSegment, self.segments.items, {}, segmentSortCompare); | |
| 135 | ||
| 136 | for (self.segments.items) |firstSegment, i| { | |
| 137 | if (firstSegment.firstSection) |firstSection| { | |
| 138 | const diff = firstSection.elfOffset - firstSegment.elfOffset; | |
| 139 | ||
| 140 | firstSegment.elfOffset += diff; | |
| 141 | firstSegment.fileSize += diff; | |
| 142 | firstSegment.physicalAddress += diff; | |
| 143 | ||
| 144 | const basePhysicalAddress = firstSegment.physicalAddress; | |
| 145 | ||
| 146 | for (self.segments.items[i + 1 ..]) |segment| { | |
| 147 | segment.binaryOffset = segment.physicalAddress - basePhysicalAddress; | |
| 148 | } | |
| 149 | break; | |
| 150 | } | |
| 151 | } | |
| 152 | ||
| 153 | for (self.sections.items) |section| { | |
| 154 | if (section.segment) |segment| { | |
| 155 | section.binaryOffset = segment.binaryOffset + (section.elfOffset - segment.elfOffset); | |
| 156 | } | |
| 157 | } | |
| 158 | ||
| 159 | sort.sort(*BinaryElfSection, self.sections.items, {}, sectionSortCompare); | |
| 160 | ||
| 161 | return self; | |
| 162 | } | |
| 163 | ||
| 164 | fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.Elf64_Phdr) bool { | |
| 165 | return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize); | |
| 166 | } | |
| 167 | ||
| 168 | fn sectionValidForOutput(shdr: anytype) bool { | |
| 169 | return shdr.sh_size > 0 and shdr.sh_type != elf.SHT_NOBITS and | |
| 170 | ((shdr.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC); | |
| 171 | } | |
| 172 | ||
| 173 | fn segmentSortCompare(context: void, left: *BinaryElfSegment, right: *BinaryElfSegment) bool { | |
| 174 | _ = context; | |
| 175 | if (left.physicalAddress < right.physicalAddress) { | |
| 176 | return true; | |
| 177 | } | |
| 178 | if (left.physicalAddress > right.physicalAddress) { | |
| 179 | return false; | |
| 180 | } | |
| 181 | return false; | |
| 182 | } | |
| 183 | ||
| 184 | fn sectionSortCompare(context: void, left: *BinaryElfSection, right: *BinaryElfSection) bool { | |
| 185 | _ = context; | |
| 186 | return left.binaryOffset < right.binaryOffset; | |
| 187 | } | |
| 188 | }; | |
| 189 | ||
| 190 | fn writeBinaryElfSection(elf_file: File, out_file: File, section: *BinaryElfSection) !void { | |
| 191 | try out_file.writeFileAll(elf_file, .{ | |
| 192 | .in_offset = section.elfOffset, | |
| 193 | .in_len = section.fileSize, | |
| 194 | }); | |
| 195 | } | |
| 196 | ||
| 197 | const HexWriter = struct { | |
| 198 | prev_addr: ?u32 = null, | |
| 199 | out_file: File, | |
| 200 | ||
| 201 | /// Max data bytes per line of output | |
| 202 | const MAX_PAYLOAD_LEN: u8 = 16; | |
| 203 | ||
| 204 | fn addressParts(address: u16) [2]u8 { | |
| 205 | const msb = @truncate(u8, address >> 8); | |
| 206 | const lsb = @truncate(u8, address); | |
| 207 | return [2]u8{ msb, lsb }; | |
| 208 | } | |
| 209 | ||
| 210 | const Record = struct { | |
| 211 | const Type = enum(u8) { | |
| 212 | Data = 0, | |
| 213 | EOF = 1, | |
| 214 | ExtendedSegmentAddress = 2, | |
| 215 | ExtendedLinearAddress = 4, | |
| 216 | }; | |
| 217 | ||
| 218 | address: u16, | |
| 219 | payload: union(Type) { | |
| 220 | Data: []const u8, | |
| 221 | EOF: void, | |
| 222 | ExtendedSegmentAddress: [2]u8, | |
| 223 | ExtendedLinearAddress: [2]u8, | |
| 224 | }, | |
| 225 | ||
| 226 | fn EOF() Record { | |
| 227 | return Record{ | |
| 228 | .address = 0, | |
| 229 | .payload = .EOF, | |
| 230 | }; | |
| 231 | } | |
| 232 | ||
| 233 | fn Data(address: u32, data: []const u8) Record { | |
| 234 | return Record{ | |
| 235 | .address = @intCast(u16, address % 0x10000), | |
| 236 | .payload = .{ .Data = data }, | |
| 237 | }; | |
| 238 | } | |
| 239 | ||
| 240 | fn Address(address: u32) Record { | |
| 241 | std.debug.assert(address > 0xFFFF); | |
| 242 | const segment = @intCast(u16, address / 0x10000); | |
| 243 | if (address > 0xFFFFF) { | |
| 244 | return Record{ | |
| 245 | .address = 0, | |
| 246 | .payload = .{ .ExtendedLinearAddress = addressParts(segment) }, | |
| 247 | }; | |
| 248 | } else { | |
| 249 | return Record{ | |
| 250 | .address = 0, | |
| 251 | .payload = .{ .ExtendedSegmentAddress = addressParts(segment << 12) }, | |
| 252 | }; | |
| 253 | } | |
| 254 | } | |
| 255 | ||
| 256 | fn getPayloadBytes(self: Record) []const u8 { | |
| 257 | return switch (self.payload) { | |
| 258 | .Data => |d| d, | |
| 259 | .EOF => @as([]const u8, &.{}), | |
| 260 | .ExtendedSegmentAddress, .ExtendedLinearAddress => |*seg| seg, | |
| 261 | }; | |
| 262 | } | |
| 263 | ||
| 264 | fn checksum(self: Record) u8 { | |
| 265 | const payload_bytes = self.getPayloadBytes(); | |
| 266 | ||
| 267 | var sum: u8 = @intCast(u8, payload_bytes.len); | |
| 268 | const parts = addressParts(self.address); | |
| 269 | sum +%= parts[0]; | |
| 270 | sum +%= parts[1]; | |
| 271 | sum +%= @enumToInt(self.payload); | |
| 272 | for (payload_bytes) |byte| { | |
| 273 | sum +%= byte; | |
| 274 | } | |
| 275 | return (sum ^ 0xFF) +% 1; | |
| 276 | } | |
| 277 | ||
| 278 | fn write(self: Record, file: File) File.WriteError!void { | |
| 279 | const linesep = "\r\n"; | |
| 280 | // colon, (length, address, type, payload, checksum) as hex, CRLF | |
| 281 | const BUFSIZE = 1 + (1 + 2 + 1 + MAX_PAYLOAD_LEN + 1) * 2 + linesep.len; | |
| 282 | var outbuf: [BUFSIZE]u8 = undefined; | |
| 283 | const payload_bytes = self.getPayloadBytes(); | |
| 284 | std.debug.assert(payload_bytes.len <= MAX_PAYLOAD_LEN); | |
| 285 | ||
| 286 | const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3s}{4X:0>2}" ++ linesep, .{ | |
| 287 | @intCast(u8, payload_bytes.len), | |
| 288 | self.address, | |
| 289 | @enumToInt(self.payload), | |
| 290 | std.fmt.fmtSliceHexUpper(payload_bytes), | |
| 291 | self.checksum(), | |
| 292 | }); | |
| 293 | try file.writeAll(line); | |
| 294 | } | |
| 295 | }; | |
| 296 | ||
| 297 | pub fn writeSegment(self: *HexWriter, segment: *const BinaryElfSegment, elf_file: File) !void { | |
| 298 | var buf: [MAX_PAYLOAD_LEN]u8 = undefined; | |
| 299 | var bytes_read: usize = 0; | |
| 300 | while (bytes_read < segment.fileSize) { | |
| 301 | const row_address = @intCast(u32, segment.physicalAddress + bytes_read); | |
| 302 | ||
| 303 | const remaining = segment.fileSize - bytes_read; | |
| 304 | const to_read = @intCast(usize, @min(remaining, MAX_PAYLOAD_LEN)); | |
| 305 | const did_read = try elf_file.preadAll(buf[0..to_read], segment.elfOffset + bytes_read); | |
| 306 | if (did_read < to_read) return error.UnexpectedEOF; | |
| 307 | ||
| 308 | try self.writeDataRow(row_address, buf[0..did_read]); | |
| 309 | ||
| 310 | bytes_read += did_read; | |
| 311 | } | |
| 312 | } | |
| 313 | ||
| 314 | fn writeDataRow(self: *HexWriter, address: u32, data: []const u8) File.WriteError!void { | |
| 315 | const record = Record.Data(address, data); | |
| 316 | if (address > 0xFFFF and (self.prev_addr == null or record.address != self.prev_addr.?)) { | |
| 317 | try Record.Address(address).write(self.out_file); | |
| 318 | } | |
| 319 | try record.write(self.out_file); | |
| 320 | self.prev_addr = @intCast(u32, record.address + data.len); | |
| 321 | } | |
| 322 | ||
| 323 | fn writeEOF(self: HexWriter) File.WriteError!void { | |
| 324 | try Record.EOF().write(self.out_file); | |
| 325 | } | |
| 326 | }; | |
| 327 | ||
| 328 | fn containsValidAddressRange(segments: []*BinaryElfSegment) bool { | |
| 329 | const max_address = std.math.maxInt(u32); | |
| 330 | for (segments) |segment| { | |
| 331 | if (segment.fileSize > max_address or | |
| 332 | segment.physicalAddress > max_address - segment.fileSize) return false; | |
| 333 | } | |
| 334 | return true; | |
| 335 | } | |
| 336 | ||
| 337 | fn padFile(f: fs.File, size: ?usize) !void { | |
| 338 | if (size) |pad_size| { | |
| 339 | const current_size = try f.getEndPos(); | |
| 340 | if (current_size < pad_size) { | |
| 341 | try f.seekTo(pad_size - 1); | |
| 342 | try f.writer().writeByte(0); | |
| 343 | } | |
| 344 | if (current_size > pad_size) { | |
| 345 | return error.FileTooLarge; // Maybe this shouldn't be an error? | |
| 346 | } | |
| 347 | } | |
| 348 | } | |
| 349 | ||
| 350 | fn emitRaw(allocator: Allocator, elf_path: []const u8, raw_path: []const u8, options: CreateOptions) !void { | |
| 351 | var elf_file = try fs.cwd().openFile(elf_path, .{}); | |
| 352 | defer elf_file.close(); | |
| 353 | ||
| 354 | var out_file = try fs.cwd().createFile(raw_path, .{}); | |
| 355 | defer out_file.close(); | |
| 356 | ||
| 357 | var binary_elf_output = try BinaryElfOutput.parse(allocator, elf_file); | |
| 358 | defer binary_elf_output.deinit(); | |
| 359 | ||
| 360 | const effective_format = options.format orelse detectFormat(raw_path); | |
| 361 | ||
| 362 | if (options.only_section_name) |target_name| { | |
| 363 | switch (effective_format) { | |
| 364 | // Hex format can only write segments/phdrs, sections not supported yet | |
| 365 | .hex => return error.NotYetImplemented, | |
| 366 | .bin => { | |
| 367 | for (binary_elf_output.sections.items) |section| { | |
| 368 | if (section.name) |curr_name| { | |
| 369 | if (!std.mem.eql(u8, curr_name, target_name)) | |
| 370 | continue; | |
| 371 | } else { | |
| 372 | continue; | |
| 373 | } | |
| 374 | ||
| 375 | try writeBinaryElfSection(elf_file, out_file, section); | |
| 376 | try padFile(out_file, options.pad_to_size); | |
| 377 | return; | |
| 378 | } | |
| 379 | }, | |
| 380 | } | |
| 381 | ||
| 382 | return error.SectionNotFound; | |
| 383 | } | |
| 384 | ||
| 385 | switch (effective_format) { | |
| 386 | .bin => { | |
| 387 | for (binary_elf_output.sections.items) |section| { | |
| 388 | try out_file.seekTo(section.binaryOffset); | |
| 389 | try writeBinaryElfSection(elf_file, out_file, section); | |
| 390 | } | |
| 391 | try padFile(out_file, options.pad_to_size); | |
| 392 | }, | |
| 393 | .hex => { | |
| 394 | if (binary_elf_output.segments.items.len == 0) return; | |
| 395 | if (!containsValidAddressRange(binary_elf_output.segments.items)) { | |
| 396 | return error.InvalidHexfileAddressRange; | |
| 397 | } | |
| 398 | ||
| 399 | var hex_writer = HexWriter{ .out_file = out_file }; | |
| 400 | for (binary_elf_output.sections.items) |section| { | |
| 401 | if (section.segment) |segment| { | |
| 402 | try hex_writer.writeSegment(segment, elf_file); | |
| 403 | } | |
| 404 | } | |
| 405 | if (options.pad_to_size) |_| { | |
| 406 | // Padding to a size in hex files isn't applicable | |
| 407 | return error.InvalidArgument; | |
| 408 | } | |
| 409 | try hex_writer.writeEOF(); | |
| 410 | }, | |
| 411 | } | |
| 412 | } | |
| 413 | ||
| 414 | const InstallRawStep = @This(); | |
| 415 | ||
| 416 | 20 | pub const base_id = .install_raw; |
| 417 | 21 | |
| 418 | 22 | pub const RawFormat = enum { |
| ... | ... | @@ -428,18 +32,11 @@ dest_filename: []const u8, |
| 428 | 32 | options: CreateOptions, |
| 429 | 33 | output_file: std.build.GeneratedFile, |
| 430 | 34 | |
| 431 | fn detectFormat(filename: []const u8) RawFormat { | |
| 432 | if (std.mem.endsWith(u8, filename, ".hex") or std.mem.endsWith(u8, filename, ".ihex")) { | |
| 433 | return .hex; | |
| 434 | } | |
| 435 | return .bin; | |
| 436 | } | |
| 437 | ||
| 438 | 35 | pub const CreateOptions = struct { |
| 439 | 36 | format: ?RawFormat = null, |
| 440 | 37 | dest_dir: ?InstallDir = null, |
| 441 | only_section_name: ?[]const u8 = null, | |
| 442 | pad_to_size: ?usize = null, | |
| 38 | only_section: ?[]const u8 = null, | |
| 39 | pad_to: ?u64 = null, | |
| 443 | 40 | }; |
| 444 | 41 | |
| 445 | 42 | pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: CreateOptions) *InstallRawStep { |
| ... | ... | @@ -470,60 +67,40 @@ pub fn getOutputSource(self: *const InstallRawStep) std.build.FileSource { |
| 470 | 67 | |
| 471 | 68 | fn make(step: *Step) !void { |
| 472 | 69 | const self = @fieldParentPtr(InstallRawStep, "step", step); |
| 473 | const builder = self.builder; | |
| 70 | const b = self.builder; | |
| 474 | 71 | |
| 475 | 72 | if (self.artifact.target.getObjectFormat() != .elf) { |
| 476 | 73 | std.debug.print("InstallRawStep only works with ELF format.\n", .{}); |
| 477 | 74 | return error.InvalidObjectFormat; |
| 478 | 75 | } |
| 479 | 76 | |
| 480 | const full_src_path = self.artifact.getOutputSource().getPath(builder); | |
| 481 | const full_dest_path = builder.getInstallPath(self.dest_dir, self.dest_filename); | |
| 482 | ||
| 483 | fs.cwd().makePath(builder.getInstallPath(self.dest_dir, "")) catch unreachable; | |
| 484 | try emitRaw(builder.allocator, full_src_path, full_dest_path, self.options); | |
| 77 | const full_src_path = self.artifact.getOutputSource().getPath(b); | |
| 78 | const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename); | |
| 485 | 79 | self.output_file.path = full_dest_path; |
| 486 | } | |
| 487 | 80 | |
| 488 | test { | |
| 489 | std.testing.refAllDecls(InstallRawStep); | |
| 490 | } | |
| 81 | fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")) catch unreachable; | |
| 491 | 82 | |
| 492 | test "Detect format from filename" { | |
| 493 | try std.testing.expectEqual(RawFormat.hex, detectFormat("foo.hex")); | |
| 494 | try std.testing.expectEqual(RawFormat.hex, detectFormat("foo.ihex")); | |
| 495 | try std.testing.expectEqual(RawFormat.bin, detectFormat("foo.bin")); | |
| 496 | try std.testing.expectEqual(RawFormat.bin, detectFormat("foo.bar")); | |
| 497 | try std.testing.expectEqual(RawFormat.bin, detectFormat("a")); | |
| 498 | } | |
| 83 | var argv_list = std.ArrayList([]const u8).init(b.allocator); | |
| 84 | try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" }); | |
| 499 | 85 | |
| 500 | test "containsValidAddressRange" { | |
| 501 | var segment = BinaryElfSegment{ | |
| 502 | .physicalAddress = 0, | |
| 503 | .virtualAddress = 0, | |
| 504 | .elfOffset = 0, | |
| 505 | .binaryOffset = 0, | |
| 506 | .fileSize = 0, | |
| 507 | .firstSection = null, | |
| 86 | if (self.options.only_section) |only_section| { | |
| 87 | try argv_list.appendSlice(&.{ "-j", only_section }); | |
| 88 | } | |
| 89 | if (self.options.pad_to) |pad_to| { | |
| 90 | try argv_list.appendSlice(&.{ | |
| 91 | "--pad-to", | |
| 92 | b.fmt("{d}", .{pad_to}), | |
| 93 | }); | |
| 94 | } | |
| 95 | if (self.options.format) |format| switch (format) { | |
| 96 | .bin => try argv_list.appendSlice(&.{ "-O", "binary" }), | |
| 97 | .hex => try argv_list.appendSlice(&.{ "-O", "hex" }), | |
| 508 | 98 | }; |
| 509 | var buf: [1]*BinaryElfSegment = .{&segment}; | |
| 510 | ||
| 511 | // segment too big | |
| 512 | segment.fileSize = std.math.maxInt(u32) + 1; | |
| 513 | try std.testing.expect(!containsValidAddressRange(&buf)); | |
| 514 | ||
| 515 | // start address too big | |
| 516 | segment.physicalAddress = std.math.maxInt(u32) + 1; | |
| 517 | segment.fileSize = 2; | |
| 518 | try std.testing.expect(!containsValidAddressRange(&buf)); | |
| 519 | 99 | |
| 520 | // max address too big | |
| 521 | segment.physicalAddress = std.math.maxInt(u32) - 1; | |
| 522 | segment.fileSize = 2; | |
| 523 | try std.testing.expect(!containsValidAddressRange(&buf)); | |
| 100 | try argv_list.appendSlice(&.{ full_src_path, full_dest_path }); | |
| 101 | _ = try self.builder.execFromStep(argv_list.items, &self.step); | |
| 102 | } | |
| 524 | 103 | |
| 525 | // is ok | |
| 526 | segment.physicalAddress = std.math.maxInt(u32) - 1; | |
| 527 | segment.fileSize = 1; | |
| 528 | try std.testing.expect(containsValidAddressRange(&buf)); | |
| 104 | test { | |
| 105 | std.testing.refAllDecls(InstallRawStep); | |
| 529 | 106 | } |
src/main.zig+3| ... | ... | @@ -85,6 +85,7 @@ const normal_usage = |
| 85 | 85 | \\ dlltool Use Zig as a drop-in dlltool.exe |
| 86 | 86 | \\ lib Use Zig as a drop-in lib.exe |
| 87 | 87 | \\ ranlib Use Zig as a drop-in ranlib |
| 88 | \\ objcopy Use Zig as a drop-in objcopy | |
| 88 | 89 | \\ |
| 89 | 90 | \\ env Print lib path, std path, cache directory, and version |
| 90 | 91 | \\ help Print this help and exit |
| ... | ... | @@ -286,6 +287,8 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 286 | 287 | return cmdBuild(gpa, arena, cmd_args); |
| 287 | 288 | } else if (mem.eql(u8, cmd, "fmt")) { |
| 288 | 289 | return cmdFmt(gpa, arena, cmd_args); |
| 290 | } else if (mem.eql(u8, cmd, "objcopy")) { | |
| 291 | return @import("objcopy.zig").cmdObjCopy(gpa, arena, cmd_args); | |
| 289 | 292 | } else if (mem.eql(u8, cmd, "libc")) { |
| 290 | 293 | return cmdLibC(gpa, cmd_args); |
| 291 | 294 | } else if (mem.eql(u8, cmd, "init-exe")) { |
src/objcopy.zig created+550| ... | ... | @@ -0,0 +1,550 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | const fs = std.fs; | |
| 4 | const elf = std.elf; | |
| 5 | const Allocator = std.mem.Allocator; | |
| 6 | const File = std.fs.File; | |
| 7 | const main = @import("main.zig"); | |
| 8 | const fatal = main.fatal; | |
| 9 | const cleanExit = main.cleanExit; | |
| 10 | ||
| 11 | pub fn cmdObjCopy( | |
| 12 | gpa: Allocator, | |
| 13 | arena: Allocator, | |
| 14 | args: []const []const u8, | |
| 15 | ) !void { | |
| 16 | _ = gpa; | |
| 17 | var i: usize = 0; | |
| 18 | var opt_out_fmt: ?std.Target.ObjectFormat = null; | |
| 19 | var opt_input: ?[]const u8 = null; | |
| 20 | var opt_output: ?[]const u8 = null; | |
| 21 | var only_section: ?[]const u8 = null; | |
| 22 | var pad_to: ?u64 = null; | |
| 23 | while (i < args.len) : (i += 1) { | |
| 24 | const arg = args[i]; | |
| 25 | if (!mem.startsWith(u8, arg, "-")) { | |
| 26 | if (opt_input == null) { | |
| 27 | opt_input = arg; | |
| 28 | } else if (opt_output == null) { | |
| 29 | opt_output = arg; | |
| 30 | } else { | |
| 31 | fatal("unexpected positional argument: '{s}'", .{arg}); | |
| 32 | } | |
| 33 | } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { | |
| 34 | return std.io.getStdOut().writeAll(usage); | |
| 35 | } else if (mem.eql(u8, arg, "-O") or mem.eql(u8, arg, "--output-target")) { | |
| 36 | i += 1; | |
| 37 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); | |
| 38 | const next_arg = args[i]; | |
| 39 | if (mem.eql(u8, next_arg, "binary")) { | |
| 40 | opt_out_fmt = .raw; | |
| 41 | } else { | |
| 42 | opt_out_fmt = std.meta.stringToEnum(std.Target.ObjectFormat, next_arg) orelse | |
| 43 | fatal("invalid output format: '{s}'", .{next_arg}); | |
| 44 | } | |
| 45 | } else if (mem.startsWith(u8, arg, "--output-target=")) { | |
| 46 | const next_arg = arg["--output-target=".len..]; | |
| 47 | if (mem.eql(u8, next_arg, "binary")) { | |
| 48 | opt_out_fmt = .raw; | |
| 49 | } else { | |
| 50 | opt_out_fmt = std.meta.stringToEnum(std.Target.ObjectFormat, next_arg) orelse | |
| 51 | fatal("invalid output format: '{s}'", .{next_arg}); | |
| 52 | } | |
| 53 | } else if (mem.eql(u8, arg, "-j") or mem.eql(u8, arg, "--only-section")) { | |
| 54 | i += 1; | |
| 55 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); | |
| 56 | only_section = args[i]; | |
| 57 | } else if (mem.startsWith(u8, arg, "--only-section=")) { | |
| 58 | only_section = arg["--output-target=".len..]; | |
| 59 | } else if (mem.eql(u8, arg, "--pad-to")) { | |
| 60 | i += 1; | |
| 61 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); | |
| 62 | pad_to = std.fmt.parseInt(u64, args[i], 0) catch |err| { | |
| 63 | fatal("unable to parse: '{s}': {s}", .{ args[i], @errorName(err) }); | |
| 64 | }; | |
| 65 | } else { | |
| 66 | fatal("unrecognized argument: '{s}'", .{arg}); | |
| 67 | } | |
| 68 | } | |
| 69 | const input = opt_input orelse fatal("expected input parameter", .{}); | |
| 70 | const output = opt_output orelse fatal("expected output parameter", .{}); | |
| 71 | ||
| 72 | var in_file = fs.cwd().openFile(input, .{}) catch |err| | |
| 73 | fatal("unable to open '{s}': {s}", .{ input, @errorName(err) }); | |
| 74 | defer in_file.close(); | |
| 75 | ||
| 76 | var out_file = try fs.cwd().createFile(output, .{}); | |
| 77 | defer out_file.close(); | |
| 78 | ||
| 79 | const elf_hdr = std.elf.Header.read(in_file) catch |err| switch (err) { | |
| 80 | error.InvalidElfMagic => fatal("not an ELF file: '{s}'", .{input}), | |
| 81 | else => fatal("unable to read '{s}': {s}", .{ input, @errorName(err) }), | |
| 82 | }; | |
| 83 | ||
| 84 | const in_ofmt = .elf; | |
| 85 | ||
| 86 | const out_fmt: std.Target.ObjectFormat = opt_out_fmt orelse ofmt: { | |
| 87 | if (mem.endsWith(u8, output, ".hex") or std.mem.endsWith(u8, output, ".ihex")) { | |
| 88 | break :ofmt .hex; | |
| 89 | } else if (mem.endsWith(u8, output, ".bin")) { | |
| 90 | break :ofmt .raw; | |
| 91 | } else if (mem.endsWith(u8, output, ".elf")) { | |
| 92 | break :ofmt .elf; | |
| 93 | } else { | |
| 94 | break :ofmt in_ofmt; | |
| 95 | } | |
| 96 | }; | |
| 97 | ||
| 98 | switch (out_fmt) { | |
| 99 | .hex, .raw, .elf => { | |
| 100 | try emitElf(arena, in_file, out_file, elf_hdr, .{ | |
| 101 | .ofmt = out_fmt, | |
| 102 | .only_section = only_section, | |
| 103 | .pad_to = pad_to, | |
| 104 | }); | |
| 105 | return cleanExit(); | |
| 106 | }, | |
| 107 | else => fatal("unsupported output object format: {s}", .{@tagName(out_fmt)}), | |
| 108 | } | |
| 109 | } | |
| 110 | ||
| 111 | const usage = | |
| 112 | \\Usage: zig objcopy [options] input output | |
| 113 | \\ | |
| 114 | \\Options: | |
| 115 | \\ -h, --help Print this help and exit | |
| 116 | \\ --output-target=<value> Format of the output file | |
| 117 | \\ -O <value> Alias for --output-target | |
| 118 | \\ --only-section=<section> Remove all but <section> | |
| 119 | \\ -j <value> Alias for --only-section | |
| 120 | \\ --pad-to <addr> Pad the last section up to address <addr> | |
| 121 | \\ | |
| 122 | ; | |
| 123 | ||
| 124 | pub const EmitRawElfOptions = struct { | |
| 125 | ofmt: std.Target.ObjectFormat, | |
| 126 | only_section: ?[]const u8 = null, | |
| 127 | pad_to: ?u64 = null, | |
| 128 | }; | |
| 129 | ||
| 130 | fn emitElf( | |
| 131 | arena: Allocator, | |
| 132 | in_file: File, | |
| 133 | out_file: File, | |
| 134 | elf_hdr: elf.Header, | |
| 135 | options: EmitRawElfOptions, | |
| 136 | ) !void { | |
| 137 | var binary_elf_output = try BinaryElfOutput.parse(arena, in_file, elf_hdr); | |
| 138 | defer binary_elf_output.deinit(); | |
| 139 | ||
| 140 | if (options.ofmt == .elf) { | |
| 141 | fatal("zig objcopy: ELF to ELF copying is not implemented yet", .{}); | |
| 142 | } | |
| 143 | ||
| 144 | if (options.only_section) |target_name| { | |
| 145 | switch (options.ofmt) { | |
| 146 | .hex => fatal("zig objcopy: hex format with sections is not implemented yet", .{}), | |
| 147 | .raw => { | |
| 148 | for (binary_elf_output.sections.items) |section| { | |
| 149 | if (section.name) |curr_name| { | |
| 150 | if (!std.mem.eql(u8, curr_name, target_name)) | |
| 151 | continue; | |
| 152 | } else { | |
| 153 | continue; | |
| 154 | } | |
| 155 | ||
| 156 | try writeBinaryElfSection(in_file, out_file, section); | |
| 157 | try padFile(out_file, options.pad_to); | |
| 158 | return; | |
| 159 | } | |
| 160 | }, | |
| 161 | else => unreachable, | |
| 162 | } | |
| 163 | ||
| 164 | return error.SectionNotFound; | |
| 165 | } | |
| 166 | ||
| 167 | switch (options.ofmt) { | |
| 168 | .raw => { | |
| 169 | for (binary_elf_output.sections.items) |section| { | |
| 170 | try out_file.seekTo(section.binaryOffset); | |
| 171 | try writeBinaryElfSection(in_file, out_file, section); | |
| 172 | } | |
| 173 | try padFile(out_file, options.pad_to); | |
| 174 | }, | |
| 175 | .hex => { | |
| 176 | if (binary_elf_output.segments.items.len == 0) return; | |
| 177 | if (!containsValidAddressRange(binary_elf_output.segments.items)) { | |
| 178 | return error.InvalidHexfileAddressRange; | |
| 179 | } | |
| 180 | ||
| 181 | var hex_writer = HexWriter{ .out_file = out_file }; | |
| 182 | for (binary_elf_output.sections.items) |section| { | |
| 183 | if (section.segment) |segment| { | |
| 184 | try hex_writer.writeSegment(segment, in_file); | |
| 185 | } | |
| 186 | } | |
| 187 | if (options.pad_to) |_| { | |
| 188 | // Padding to a size in hex files isn't applicable | |
| 189 | return error.InvalidArgument; | |
| 190 | } | |
| 191 | try hex_writer.writeEOF(); | |
| 192 | }, | |
| 193 | else => unreachable, | |
| 194 | } | |
| 195 | } | |
| 196 | ||
| 197 | const BinaryElfSection = struct { | |
| 198 | elfOffset: u64, | |
| 199 | binaryOffset: u64, | |
| 200 | fileSize: usize, | |
| 201 | name: ?[]const u8, | |
| 202 | segment: ?*BinaryElfSegment, | |
| 203 | }; | |
| 204 | ||
| 205 | const BinaryElfSegment = struct { | |
| 206 | physicalAddress: u64, | |
| 207 | virtualAddress: u64, | |
| 208 | elfOffset: u64, | |
| 209 | binaryOffset: u64, | |
| 210 | fileSize: u64, | |
| 211 | firstSection: ?*BinaryElfSection, | |
| 212 | }; | |
| 213 | ||
| 214 | const BinaryElfOutput = struct { | |
| 215 | segments: std.ArrayListUnmanaged(*BinaryElfSegment), | |
| 216 | sections: std.ArrayListUnmanaged(*BinaryElfSection), | |
| 217 | allocator: Allocator, | |
| 218 | shstrtab: ?[]const u8, | |
| 219 | ||
| 220 | const Self = @This(); | |
| 221 | ||
| 222 | pub fn deinit(self: *Self) void { | |
| 223 | if (self.shstrtab) |shstrtab| | |
| 224 | self.allocator.free(shstrtab); | |
| 225 | self.sections.deinit(self.allocator); | |
| 226 | self.segments.deinit(self.allocator); | |
| 227 | } | |
| 228 | ||
| 229 | pub fn parse(allocator: Allocator, elf_file: File, elf_hdr: elf.Header) !Self { | |
| 230 | var self: Self = .{ | |
| 231 | .segments = .{}, | |
| 232 | .sections = .{}, | |
| 233 | .allocator = allocator, | |
| 234 | .shstrtab = null, | |
| 235 | }; | |
| 236 | errdefer self.sections.deinit(allocator); | |
| 237 | errdefer self.segments.deinit(allocator); | |
| 238 | ||
| 239 | self.shstrtab = blk: { | |
| 240 | if (elf_hdr.shstrndx >= elf_hdr.shnum) break :blk null; | |
| 241 | ||
| 242 | var section_headers = elf_hdr.section_header_iterator(&elf_file); | |
| 243 | ||
| 244 | var section_counter: usize = 0; | |
| 245 | while (section_counter < elf_hdr.shstrndx) : (section_counter += 1) { | |
| 246 | _ = (try section_headers.next()).?; | |
| 247 | } | |
| 248 | ||
| 249 | const shstrtab_shdr = (try section_headers.next()).?; | |
| 250 | ||
| 251 | const buffer = try allocator.alloc(u8, @intCast(usize, shstrtab_shdr.sh_size)); | |
| 252 | errdefer allocator.free(buffer); | |
| 253 | ||
| 254 | const num_read = try elf_file.preadAll(buffer, shstrtab_shdr.sh_offset); | |
| 255 | if (num_read != buffer.len) return error.EndOfStream; | |
| 256 | ||
| 257 | break :blk buffer; | |
| 258 | }; | |
| 259 | ||
| 260 | errdefer if (self.shstrtab) |shstrtab| allocator.free(shstrtab); | |
| 261 | ||
| 262 | var section_headers = elf_hdr.section_header_iterator(&elf_file); | |
| 263 | while (try section_headers.next()) |section| { | |
| 264 | if (sectionValidForOutput(section)) { | |
| 265 | const newSection = try allocator.create(BinaryElfSection); | |
| 266 | ||
| 267 | newSection.binaryOffset = 0; | |
| 268 | newSection.elfOffset = section.sh_offset; | |
| 269 | newSection.fileSize = @intCast(usize, section.sh_size); | |
| 270 | newSection.segment = null; | |
| 271 | ||
| 272 | newSection.name = if (self.shstrtab) |shstrtab| | |
| 273 | std.mem.span(@ptrCast([*:0]const u8, &shstrtab[section.sh_name])) | |
| 274 | else | |
| 275 | null; | |
| 276 | ||
| 277 | try self.sections.append(allocator, newSection); | |
| 278 | } | |
| 279 | } | |
| 280 | ||
| 281 | var program_headers = elf_hdr.program_header_iterator(&elf_file); | |
| 282 | while (try program_headers.next()) |phdr| { | |
| 283 | if (phdr.p_type == elf.PT_LOAD) { | |
| 284 | const newSegment = try allocator.create(BinaryElfSegment); | |
| 285 | ||
| 286 | newSegment.physicalAddress = if (phdr.p_paddr != 0) phdr.p_paddr else phdr.p_vaddr; | |
| 287 | newSegment.virtualAddress = phdr.p_vaddr; | |
| 288 | newSegment.fileSize = @intCast(usize, phdr.p_filesz); | |
| 289 | newSegment.elfOffset = phdr.p_offset; | |
| 290 | newSegment.binaryOffset = 0; | |
| 291 | newSegment.firstSection = null; | |
| 292 | ||
| 293 | for (self.sections.items) |section| { | |
| 294 | if (sectionWithinSegment(section, phdr)) { | |
| 295 | if (section.segment) |sectionSegment| { | |
| 296 | if (sectionSegment.elfOffset > newSegment.elfOffset) { | |
| 297 | section.segment = newSegment; | |
| 298 | } | |
| 299 | } else { | |
| 300 | section.segment = newSegment; | |
| 301 | } | |
| 302 | ||
| 303 | if (newSegment.firstSection == null) { | |
| 304 | newSegment.firstSection = section; | |
| 305 | } | |
| 306 | } | |
| 307 | } | |
| 308 | ||
| 309 | try self.segments.append(allocator, newSegment); | |
| 310 | } | |
| 311 | } | |
| 312 | ||
| 313 | std.sort.sort(*BinaryElfSegment, self.segments.items, {}, segmentSortCompare); | |
| 314 | ||
| 315 | for (self.segments.items) |firstSegment, i| { | |
| 316 | if (firstSegment.firstSection) |firstSection| { | |
| 317 | const diff = firstSection.elfOffset - firstSegment.elfOffset; | |
| 318 | ||
| 319 | firstSegment.elfOffset += diff; | |
| 320 | firstSegment.fileSize += diff; | |
| 321 | firstSegment.physicalAddress += diff; | |
| 322 | ||
| 323 | const basePhysicalAddress = firstSegment.physicalAddress; | |
| 324 | ||
| 325 | for (self.segments.items[i + 1 ..]) |segment| { | |
| 326 | segment.binaryOffset = segment.physicalAddress - basePhysicalAddress; | |
| 327 | } | |
| 328 | break; | |
| 329 | } | |
| 330 | } | |
| 331 | ||
| 332 | for (self.sections.items) |section| { | |
| 333 | if (section.segment) |segment| { | |
| 334 | section.binaryOffset = segment.binaryOffset + (section.elfOffset - segment.elfOffset); | |
| 335 | } | |
| 336 | } | |
| 337 | ||
| 338 | std.sort.sort(*BinaryElfSection, self.sections.items, {}, sectionSortCompare); | |
| 339 | ||
| 340 | return self; | |
| 341 | } | |
| 342 | ||
| 343 | fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.Elf64_Phdr) bool { | |
| 344 | return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize); | |
| 345 | } | |
| 346 | ||
| 347 | fn sectionValidForOutput(shdr: anytype) bool { | |
| 348 | return shdr.sh_size > 0 and shdr.sh_type != elf.SHT_NOBITS and | |
| 349 | ((shdr.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC); | |
| 350 | } | |
| 351 | ||
| 352 | fn segmentSortCompare(context: void, left: *BinaryElfSegment, right: *BinaryElfSegment) bool { | |
| 353 | _ = context; | |
| 354 | if (left.physicalAddress < right.physicalAddress) { | |
| 355 | return true; | |
| 356 | } | |
| 357 | if (left.physicalAddress > right.physicalAddress) { | |
| 358 | return false; | |
| 359 | } | |
| 360 | return false; | |
| 361 | } | |
| 362 | ||
| 363 | fn sectionSortCompare(context: void, left: *BinaryElfSection, right: *BinaryElfSection) bool { | |
| 364 | _ = context; | |
| 365 | return left.binaryOffset < right.binaryOffset; | |
| 366 | } | |
| 367 | }; | |
| 368 | ||
| 369 | fn writeBinaryElfSection(elf_file: File, out_file: File, section: *BinaryElfSection) !void { | |
| 370 | try out_file.writeFileAll(elf_file, .{ | |
| 371 | .in_offset = section.elfOffset, | |
| 372 | .in_len = section.fileSize, | |
| 373 | }); | |
| 374 | } | |
| 375 | ||
| 376 | const HexWriter = struct { | |
| 377 | prev_addr: ?u32 = null, | |
| 378 | out_file: File, | |
| 379 | ||
| 380 | /// Max data bytes per line of output | |
| 381 | const MAX_PAYLOAD_LEN: u8 = 16; | |
| 382 | ||
| 383 | fn addressParts(address: u16) [2]u8 { | |
| 384 | const msb = @truncate(u8, address >> 8); | |
| 385 | const lsb = @truncate(u8, address); | |
| 386 | return [2]u8{ msb, lsb }; | |
| 387 | } | |
| 388 | ||
| 389 | const Record = struct { | |
| 390 | const Type = enum(u8) { | |
| 391 | Data = 0, | |
| 392 | EOF = 1, | |
| 393 | ExtendedSegmentAddress = 2, | |
| 394 | ExtendedLinearAddress = 4, | |
| 395 | }; | |
| 396 | ||
| 397 | address: u16, | |
| 398 | payload: union(Type) { | |
| 399 | Data: []const u8, | |
| 400 | EOF: void, | |
| 401 | ExtendedSegmentAddress: [2]u8, | |
| 402 | ExtendedLinearAddress: [2]u8, | |
| 403 | }, | |
| 404 | ||
| 405 | fn EOF() Record { | |
| 406 | return Record{ | |
| 407 | .address = 0, | |
| 408 | .payload = .EOF, | |
| 409 | }; | |
| 410 | } | |
| 411 | ||
| 412 | fn Data(address: u32, data: []const u8) Record { | |
| 413 | return Record{ | |
| 414 | .address = @intCast(u16, address % 0x10000), | |
| 415 | .payload = .{ .Data = data }, | |
| 416 | }; | |
| 417 | } | |
| 418 | ||
| 419 | fn Address(address: u32) Record { | |
| 420 | std.debug.assert(address > 0xFFFF); | |
| 421 | const segment = @intCast(u16, address / 0x10000); | |
| 422 | if (address > 0xFFFFF) { | |
| 423 | return Record{ | |
| 424 | .address = 0, | |
| 425 | .payload = .{ .ExtendedLinearAddress = addressParts(segment) }, | |
| 426 | }; | |
| 427 | } else { | |
| 428 | return Record{ | |
| 429 | .address = 0, | |
| 430 | .payload = .{ .ExtendedSegmentAddress = addressParts(segment << 12) }, | |
| 431 | }; | |
| 432 | } | |
| 433 | } | |
| 434 | ||
| 435 | fn getPayloadBytes(self: Record) []const u8 { | |
| 436 | return switch (self.payload) { | |
| 437 | .Data => |d| d, | |
| 438 | .EOF => @as([]const u8, &.{}), | |
| 439 | .ExtendedSegmentAddress, .ExtendedLinearAddress => |*seg| seg, | |
| 440 | }; | |
| 441 | } | |
| 442 | ||
| 443 | fn checksum(self: Record) u8 { | |
| 444 | const payload_bytes = self.getPayloadBytes(); | |
| 445 | ||
| 446 | var sum: u8 = @intCast(u8, payload_bytes.len); | |
| 447 | const parts = addressParts(self.address); | |
| 448 | sum +%= parts[0]; | |
| 449 | sum +%= parts[1]; | |
| 450 | sum +%= @enumToInt(self.payload); | |
| 451 | for (payload_bytes) |byte| { | |
| 452 | sum +%= byte; | |
| 453 | } | |
| 454 | return (sum ^ 0xFF) +% 1; | |
| 455 | } | |
| 456 | ||
| 457 | fn write(self: Record, file: File) File.WriteError!void { | |
| 458 | const linesep = "\r\n"; | |
| 459 | // colon, (length, address, type, payload, checksum) as hex, CRLF | |
| 460 | const BUFSIZE = 1 + (1 + 2 + 1 + MAX_PAYLOAD_LEN + 1) * 2 + linesep.len; | |
| 461 | var outbuf: [BUFSIZE]u8 = undefined; | |
| 462 | const payload_bytes = self.getPayloadBytes(); | |
| 463 | std.debug.assert(payload_bytes.len <= MAX_PAYLOAD_LEN); | |
| 464 | ||
| 465 | const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3s}{4X:0>2}" ++ linesep, .{ | |
| 466 | @intCast(u8, payload_bytes.len), | |
| 467 | self.address, | |
| 468 | @enumToInt(self.payload), | |
| 469 | std.fmt.fmtSliceHexUpper(payload_bytes), | |
| 470 | self.checksum(), | |
| 471 | }); | |
| 472 | try file.writeAll(line); | |
| 473 | } | |
| 474 | }; | |
| 475 | ||
| 476 | pub fn writeSegment(self: *HexWriter, segment: *const BinaryElfSegment, elf_file: File) !void { | |
| 477 | var buf: [MAX_PAYLOAD_LEN]u8 = undefined; | |
| 478 | var bytes_read: usize = 0; | |
| 479 | while (bytes_read < segment.fileSize) { | |
| 480 | const row_address = @intCast(u32, segment.physicalAddress + bytes_read); | |
| 481 | ||
| 482 | const remaining = segment.fileSize - bytes_read; | |
| 483 | const to_read = @intCast(usize, @min(remaining, MAX_PAYLOAD_LEN)); | |
| 484 | const did_read = try elf_file.preadAll(buf[0..to_read], segment.elfOffset + bytes_read); | |
| 485 | if (did_read < to_read) return error.UnexpectedEOF; | |
| 486 | ||
| 487 | try self.writeDataRow(row_address, buf[0..did_read]); | |
| 488 | ||
| 489 | bytes_read += did_read; | |
| 490 | } | |
| 491 | } | |
| 492 | ||
| 493 | fn writeDataRow(self: *HexWriter, address: u32, data: []const u8) File.WriteError!void { | |
| 494 | const record = Record.Data(address, data); | |
| 495 | if (address > 0xFFFF and (self.prev_addr == null or record.address != self.prev_addr.?)) { | |
| 496 | try Record.Address(address).write(self.out_file); | |
| 497 | } | |
| 498 | try record.write(self.out_file); | |
| 499 | self.prev_addr = @intCast(u32, record.address + data.len); | |
| 500 | } | |
| 501 | ||
| 502 | fn writeEOF(self: HexWriter) File.WriteError!void { | |
| 503 | try Record.EOF().write(self.out_file); | |
| 504 | } | |
| 505 | }; | |
| 506 | ||
| 507 | fn containsValidAddressRange(segments: []*BinaryElfSegment) bool { | |
| 508 | const max_address = std.math.maxInt(u32); | |
| 509 | for (segments) |segment| { | |
| 510 | if (segment.fileSize > max_address or | |
| 511 | segment.physicalAddress > max_address - segment.fileSize) return false; | |
| 512 | } | |
| 513 | return true; | |
| 514 | } | |
| 515 | ||
| 516 | fn padFile(f: File, opt_size: ?u64) !void { | |
| 517 | const size = opt_size orelse return; | |
| 518 | try f.setEndPos(size); | |
| 519 | } | |
| 520 | ||
| 521 | test "containsValidAddressRange" { | |
| 522 | var segment = BinaryElfSegment{ | |
| 523 | .physicalAddress = 0, | |
| 524 | .virtualAddress = 0, | |
| 525 | .elfOffset = 0, | |
| 526 | .binaryOffset = 0, | |
| 527 | .fileSize = 0, | |
| 528 | .firstSection = null, | |
| 529 | }; | |
| 530 | var buf: [1]*BinaryElfSegment = .{&segment}; | |
| 531 | ||
| 532 | // segment too big | |
| 533 | segment.fileSize = std.math.maxInt(u32) + 1; | |
| 534 | try std.testing.expect(!containsValidAddressRange(&buf)); | |
| 535 | ||
| 536 | // start address too big | |
| 537 | segment.physicalAddress = std.math.maxInt(u32) + 1; | |
| 538 | segment.fileSize = 2; | |
| 539 | try std.testing.expect(!containsValidAddressRange(&buf)); | |
| 540 | ||
| 541 | // max address too big | |
| 542 | segment.physicalAddress = std.math.maxInt(u32) - 1; | |
| 543 | segment.fileSize = 2; | |
| 544 | try std.testing.expect(!containsValidAddressRange(&buf)); | |
| 545 | ||
| 546 | // is ok | |
| 547 | segment.physicalAddress = std.math.maxInt(u32) - 1; | |
| 548 | segment.fileSize = 1; | |
| 549 | try std.testing.expect(containsValidAddressRange(&buf)); | |
| 550 | } |
src/print_targets.zig+1-1| ... | ... | @@ -3,7 +3,7 @@ const fs = std.fs; |
| 3 | 3 | const io = std.io; |
| 4 | 4 | const mem = std.mem; |
| 5 | 5 | const meta = std.meta; |
| 6 | const Allocator = mem.Allocator; | |
| 6 | const Allocator = std.mem.Allocator; | |
| 7 | 7 | const Target = std.Target; |
| 8 | 8 | const target = @import("target.zig"); |
| 9 | 9 | const assert = std.debug.assert; |