| 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,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 | const std = @import("std"); | 4 | const std = @import("std"); |
| 5 | const InstallRawStep = @This(); | ||
| 2 | 6 | ||
| 3 | const Allocator = std.mem.Allocator; | 7 | const Allocator = std.mem.Allocator; |
| 4 | const ArenaAllocator = std.heap.ArenaAllocator; | 8 | const ArenaAllocator = std.heap.ArenaAllocator; |
| ... | @@ -13,406 +17,6 @@ const fs = std.fs; | ... | @@ -13,406 +17,6 @@ const fs = std.fs; |
| 13 | const io = std.io; | 17 | const io = std.io; |
| 14 | const sort = std.sort; | 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 | pub const base_id = .install_raw; | 20 | pub const base_id = .install_raw; |
| 417 | 21 | ||
| 418 | pub const RawFormat = enum { | 22 | pub const RawFormat = enum { |
| ... | @@ -428,18 +32,11 @@ dest_filename: []const u8, | ... | @@ -428,18 +32,11 @@ dest_filename: []const u8, |
| 428 | options: CreateOptions, | 32 | options: CreateOptions, |
| 429 | output_file: std.build.GeneratedFile, | 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 | pub const CreateOptions = struct { | 35 | pub const CreateOptions = struct { |
| 439 | format: ?RawFormat = null, | 36 | format: ?RawFormat = null, |
| 440 | dest_dir: ?InstallDir = null, | 37 | dest_dir: ?InstallDir = null, |
| 441 | only_section_name: ?[]const u8 = null, | 38 | only_section: ?[]const u8 = null, |
| 442 | pad_to_size: ?usize = null, | 39 | pad_to: ?u64 = null, |
| 443 | }; | 40 | }; |
| 444 | 41 | ||
| 445 | pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: CreateOptions) *InstallRawStep { | 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,60 +67,40 @@ pub fn getOutputSource(self: *const InstallRawStep) std.build.FileSource { |
| 470 | 67 | ||
| 471 | fn make(step: *Step) !void { | 68 | fn make(step: *Step) !void { |
| 472 | const self = @fieldParentPtr(InstallRawStep, "step", step); | 69 | const self = @fieldParentPtr(InstallRawStep, "step", step); |
| 473 | const builder = self.builder; | 70 | const b = self.builder; |
| 474 | 71 | ||
| 475 | if (self.artifact.target.getObjectFormat() != .elf) { | 72 | if (self.artifact.target.getObjectFormat() != .elf) { |
| 476 | std.debug.print("InstallRawStep only works with ELF format.\n", .{}); | 73 | std.debug.print("InstallRawStep only works with ELF format.\n", .{}); |
| 477 | return error.InvalidObjectFormat; | 74 | return error.InvalidObjectFormat; |
| 478 | } | 75 | } |
| 479 | 76 | ||
| 480 | const full_src_path = self.artifact.getOutputSource().getPath(builder); | 77 | const full_src_path = self.artifact.getOutputSource().getPath(b); |
| 481 | const full_dest_path = builder.getInstallPath(self.dest_dir, self.dest_filename); | 78 | const full_dest_path = b.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); | ||
| 485 | self.output_file.path = full_dest_path; | 79 | self.output_file.path = full_dest_path; |
| 486 | } | ||
| 487 | 80 | ||
| 488 | test { | 81 | fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")) catch unreachable; |
| 489 | std.testing.refAllDecls(InstallRawStep); | ||
| 490 | } | ||
| 491 | 82 | ||
| 492 | test "Detect format from filename" { | 83 | var argv_list = std.ArrayList([]const u8).init(b.allocator); |
| 493 | try std.testing.expectEqual(RawFormat.hex, detectFormat("foo.hex")); | 84 | try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" }); |
| 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 | } | ||
| 499 | 85 | ||
| 500 | test "containsValidAddressRange" { | 86 | if (self.options.only_section) |only_section| { |
| 501 | var segment = BinaryElfSegment{ | 87 | try argv_list.appendSlice(&.{ "-j", only_section }); |
| 502 | .physicalAddress = 0, | 88 | } |
| 503 | .virtualAddress = 0, | 89 | if (self.options.pad_to) |pad_to| { |
| 504 | .elfOffset = 0, | 90 | try argv_list.appendSlice(&.{ |
| 505 | .binaryOffset = 0, | 91 | "--pad-to", |
| 506 | .fileSize = 0, | 92 | b.fmt("{d}", .{pad_to}), |
| 507 | .firstSection = null, | 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 | 100 | try argv_list.appendSlice(&.{ full_src_path, full_dest_path }); |
| 521 | segment.physicalAddress = std.math.maxInt(u32) - 1; | 101 | _ = try self.builder.execFromStep(argv_list.items, &self.step); |
| 522 | segment.fileSize = 2; | 102 | } |
| 523 | try std.testing.expect(!containsValidAddressRange(&buf)); | ||
| 524 | 103 | ||
| 525 | // is ok | 104 | test { |
| 526 | segment.physicalAddress = std.math.maxInt(u32) - 1; | 105 | std.testing.refAllDecls(InstallRawStep); |
| 527 | segment.fileSize = 1; | ||
| 528 | try std.testing.expect(containsValidAddressRange(&buf)); | ||
| 529 | } | 106 | } |
src/main.zig+3| ... | @@ -85,6 +85,7 @@ const normal_usage = | ... | @@ -85,6 +85,7 @@ const normal_usage = |
| 85 | \\ dlltool Use Zig as a drop-in dlltool.exe | 85 | \\ dlltool Use Zig as a drop-in dlltool.exe |
| 86 | \\ lib Use Zig as a drop-in lib.exe | 86 | \\ lib Use Zig as a drop-in lib.exe |
| 87 | \\ ranlib Use Zig as a drop-in ranlib | 87 | \\ ranlib Use Zig as a drop-in ranlib |
| 88 | \\ objcopy Use Zig as a drop-in objcopy | ||
| 88 | \\ | 89 | \\ |
| 89 | \\ env Print lib path, std path, cache directory, and version | 90 | \\ env Print lib path, std path, cache directory, and version |
| 90 | \\ help Print this help and exit | 91 | \\ help Print this help and exit |
| ... | @@ -286,6 +287,8 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -286,6 +287,8 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 286 | return cmdBuild(gpa, arena, cmd_args); | 287 | return cmdBuild(gpa, arena, cmd_args); |
| 287 | } else if (mem.eql(u8, cmd, "fmt")) { | 288 | } else if (mem.eql(u8, cmd, "fmt")) { |
| 288 | return cmdFmt(gpa, arena, cmd_args); | 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 | } else if (mem.eql(u8, cmd, "libc")) { | 292 | } else if (mem.eql(u8, cmd, "libc")) { |
| 290 | return cmdLibC(gpa, cmd_args); | 293 | return cmdLibC(gpa, cmd_args); |
| 291 | } else if (mem.eql(u8, cmd, "init-exe")) { | 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,7 +3,7 @@ const fs = std.fs; |
| 3 | const io = std.io; | 3 | const io = std.io; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
| 5 | const meta = std.meta; | 5 | const meta = std.meta; |
| 6 | const Allocator = mem.Allocator; | 6 | const Allocator = std.mem.Allocator; |
| 7 | const Target = std.Target; | 7 | const Target = std.Target; |
| 8 | const target = @import("target.zig"); | 8 | const target = @import("target.zig"); |
| 9 | const assert = std.debug.assert; | 9 | const assert = std.debug.assert; |