| ... | ... | @@ -2,7 +2,7 @@ const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | const Allocator = std.mem.Allocator; |
| 4 | 4 | const ArenaAllocator = std.heap.ArenaAllocator; |
| 5 | | const ArrayList = std.ArrayList; |
| 5 | const ArrayListUnmanaged = std.ArrayListUnmanaged; |
| 6 | 6 | const Builder = std.build.Builder; |
| 7 | 7 | const File = std.fs.File; |
| 8 | 8 | const InstallDir = std.build.InstallDir; |
| ... | ... | @@ -17,6 +17,7 @@ const BinaryElfSection = struct { |
| 17 | 17 | elfOffset: u64, |
| 18 | 18 | binaryOffset: u64, |
| 19 | 19 | fileSize: usize, |
| 20 | name: ?[]const u8, |
| 20 | 21 | segment: ?*BinaryElfSegment, |
| 21 | 22 | }; |
| 22 | 23 | |
| ... | ... | @@ -30,23 +31,55 @@ const BinaryElfSegment = struct { |
| 30 | 31 | }; |
| 31 | 32 | |
| 32 | 33 | const BinaryElfOutput = struct { |
| 33 | | segments: ArrayList(*BinaryElfSegment), |
| 34 | | sections: ArrayList(*BinaryElfSection), |
| 34 | segments: ArrayListUnmanaged(*BinaryElfSegment), |
| 35 | sections: ArrayListUnmanaged(*BinaryElfSection), |
| 36 | allocator: Allocator, |
| 37 | shstrtab: ?[]const u8, |
| 35 | 38 | |
| 36 | 39 | const Self = @This(); |
| 37 | 40 | |
| 38 | 41 | pub fn deinit(self: *Self) void { |
| 39 | | self.sections.deinit(); |
| 40 | | self.segments.deinit(); |
| 42 | if (self.shstrtab) |shstrtab| |
| 43 | self.allocator.free(shstrtab); |
| 44 | self.sections.deinit(self.allocator); |
| 45 | self.segments.deinit(self.allocator); |
| 41 | 46 | } |
| 42 | 47 | |
| 43 | 48 | pub fn parse(allocator: Allocator, elf_file: File) !Self { |
| 44 | 49 | var self: Self = .{ |
| 45 | | .segments = ArrayList(*BinaryElfSegment).init(allocator), |
| 46 | | .sections = ArrayList(*BinaryElfSection).init(allocator), |
| 50 | .segments = .{}, |
| 51 | .sections = .{}, |
| 52 | .allocator = allocator, |
| 53 | .shstrtab = null, |
| 47 | 54 | }; |
| 55 | errdefer self.sections.deinit(allocator); |
| 56 | errdefer self.segments.deinit(allocator); |
| 57 | |
| 48 | 58 | const elf_hdr = try std.elf.Header.read(&elf_file); |
| 49 | 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, 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 | |
| 50 | 83 | var section_headers = elf_hdr.section_header_iterator(&elf_file); |
| 51 | 84 | while (try section_headers.next()) |section| { |
| 52 | 85 | if (sectionValidForOutput(section)) { |
| ... | ... | @@ -57,7 +90,12 @@ const BinaryElfOutput = struct { |
| 57 | 90 | newSection.fileSize = @intCast(usize, section.sh_size); |
| 58 | 91 | newSection.segment = null; |
| 59 | 92 | |
| 60 | | try self.sections.append(newSection); |
| 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); |
| 61 | 99 | } |
| 62 | 100 | } |
| 63 | 101 | |
| ... | ... | @@ -89,7 +127,7 @@ const BinaryElfOutput = struct { |
| 89 | 127 | } |
| 90 | 128 | } |
| 91 | 129 | |
| 92 | | try self.segments.append(newSegment); |
| 130 | try self.segments.append(allocator, newSegment); |
| 93 | 131 | } |
| 94 | 132 | } |
| 95 | 133 | |
| ... | ... | @@ -150,8 +188,6 @@ const BinaryElfOutput = struct { |
| 150 | 188 | }; |
| 151 | 189 | |
| 152 | 190 | fn writeBinaryElfSection(elf_file: File, out_file: File, section: *BinaryElfSection) !void { |
| 153 | | try out_file.seekTo(section.binaryOffset); |
| 154 | | |
| 155 | 191 | try out_file.writeFileAll(elf_file, .{ |
| 156 | 192 | .in_offset = section.elfOffset, |
| 157 | 193 | .in_len = section.fileSize, |
| ... | ... | @@ -298,7 +334,20 @@ fn containsValidAddressRange(segments: []*BinaryElfSegment) bool { |
| 298 | 334 | return true; |
| 299 | 335 | } |
| 300 | 336 | |
| 301 | | fn emitRaw(allocator: Allocator, elf_path: []const u8, raw_path: []const u8, format: RawFormat) !void { |
| 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 { |
| 302 | 351 | var elf_file = try fs.cwd().openFile(elf_path, .{}); |
| 303 | 352 | defer elf_file.close(); |
| 304 | 353 | |
| ... | ... | @@ -308,11 +357,38 @@ fn emitRaw(allocator: Allocator, elf_path: []const u8, raw_path: []const u8, for |
| 308 | 357 | var binary_elf_output = try BinaryElfOutput.parse(allocator, elf_file); |
| 309 | 358 | defer binary_elf_output.deinit(); |
| 310 | 359 | |
| 311 | | switch (format) { |
| 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) { |
| 312 | 386 | .bin => { |
| 313 | 387 | for (binary_elf_output.sections.items) |section| { |
| 388 | try out_file.seekTo(section.binaryOffset); |
| 314 | 389 | try writeBinaryElfSection(elf_file, out_file, section); |
| 315 | 390 | } |
| 391 | try padFile(out_file, options.pad_to_size); |
| 316 | 392 | }, |
| 317 | 393 | .hex => { |
| 318 | 394 | if (binary_elf_output.segments.items.len == 0) return; |
| ... | ... | @@ -326,6 +402,10 @@ fn emitRaw(allocator: Allocator, elf_path: []const u8, raw_path: []const u8, for |
| 326 | 402 | try hex_writer.writeSegment(segment, elf_file); |
| 327 | 403 | } |
| 328 | 404 | } |
| 405 | if (options.pad_to_size) |_| { |
| 406 | // Padding to a size in hex files isn't applicable |
| 407 | return error.InvalidArgument; |
| 408 | } |
| 329 | 409 | try hex_writer.writeEOF(); |
| 330 | 410 | }, |
| 331 | 411 | } |
| ... | ... | @@ -345,7 +425,7 @@ builder: *Builder, |
| 345 | 425 | artifact: *LibExeObjStep, |
| 346 | 426 | dest_dir: InstallDir, |
| 347 | 427 | dest_filename: []const u8, |
| 348 | | format: RawFormat, |
| 428 | options: CreateOptions, |
| 349 | 429 | output_file: std.build.GeneratedFile, |
| 350 | 430 | |
| 351 | 431 | fn detectFormat(filename: []const u8) RawFormat { |
| ... | ... | @@ -358,6 +438,8 @@ fn detectFormat(filename: []const u8) RawFormat { |
| 358 | 438 | pub const CreateOptions = struct { |
| 359 | 439 | format: ?RawFormat = null, |
| 360 | 440 | dest_dir: ?InstallDir = null, |
| 441 | only_section_name: ?[]const u8 = null, |
| 442 | pad_to_size: ?usize = null, |
| 361 | 443 | }; |
| 362 | 444 | |
| 363 | 445 | pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: CreateOptions) *InstallRawStep { |
| ... | ... | @@ -373,7 +455,7 @@ pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []cons |
| 373 | 455 | .lib => unreachable, |
| 374 | 456 | }, |
| 375 | 457 | .dest_filename = dest_filename, |
| 376 | | .format = if (options.format) |f| f else detectFormat(dest_filename), |
| 458 | .options = options, |
| 377 | 459 | .output_file = std.build.GeneratedFile{ .step = &self.step }, |
| 378 | 460 | }; |
| 379 | 461 | self.step.dependOn(&artifact.step); |
| ... | ... | @@ -399,7 +481,7 @@ fn make(step: *Step) !void { |
| 399 | 481 | const full_dest_path = builder.getInstallPath(self.dest_dir, self.dest_filename); |
| 400 | 482 | |
| 401 | 483 | fs.cwd().makePath(builder.getInstallPath(self.dest_dir, "")) catch unreachable; |
| 402 | | try emitRaw(builder.allocator, full_src_path, full_dest_path, self.format); |
| 484 | try emitRaw(builder.allocator, full_src_path, full_dest_path, self.options); |
| 403 | 485 | self.output_file.path = full_dest_path; |
| 404 | 486 | } |
| 405 | 487 | |