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