blob 3d2c6124 (7945B) - Raw
1 // SPDX-License-Identifier: MIT 2 // Copyright (c) 2015-2021 Zig Contributors 3 // This file is part of [zig](https://ziglang.org/), which is MIT licensed. 4 // The MIT license requires this copyright notice to be included in all copies 5 // and substantial portions of the software. 6 const std = @import("std"); 7 8 const Allocator = std.mem.Allocator; 9 const ArenaAllocator = std.heap.ArenaAllocator; 10 const ArrayList = std.ArrayList; 11 const Builder = std.build.Builder; 12 const File = std.fs.File; 13 const InstallDir = std.build.InstallDir; 14 const LibExeObjStep = std.build.LibExeObjStep; 15 const Step = std.build.Step; 16 const elf = std.elf; 17 const fs = std.fs; 18 const io = std.io; 19 const sort = std.sort; 20 const warn = std.debug.warn; 21 22 const BinaryElfSection = struct { 23 elfOffset: u64, 24 binaryOffset: u64, 25 fileSize: usize, 26 segment: ?*BinaryElfSegment, 27 }; 28 29 const BinaryElfSegment = struct { 30 physicalAddress: u64, 31 virtualAddress: u64, 32 elfOffset: u64, 33 binaryOffset: u64, 34 fileSize: usize, 35 firstSection: ?*BinaryElfSection, 36 }; 37 38 const BinaryElfOutput = struct { 39 segments: ArrayList(*BinaryElfSegment), 40 sections: ArrayList(*BinaryElfSection), 41 42 const Self = @This(); 43 44 pub fn deinit(self: *Self) void { 45 self.sections.deinit(); 46 self.segments.deinit(); 47 } 48 49 pub fn parse(allocator: *Allocator, elf_file: File) !Self { 50 var self: Self = .{ 51 .segments = ArrayList(*BinaryElfSegment).init(allocator), 52 .sections = ArrayList(*BinaryElfSection).init(allocator), 53 }; 54 const elf_hdr = try std.elf.readHeader(elf_file); 55 56 var section_headers = elf_hdr.section_header_iterator(elf_file); 57 while (try section_headers.next()) |section| { 58 if (sectionValidForOutput(section)) { 59 const newSection = try allocator.create(BinaryElfSection); 60 61 newSection.binaryOffset = 0; 62 newSection.elfOffset = section.sh_offset; 63 newSection.fileSize = @intCast(usize, section.sh_size); 64 newSection.segment = null; 65 66 try self.sections.append(newSection); 67 } 68 } 69 70 var program_headers = elf_hdr.program_header_iterator(elf_file); 71 while (try program_headers.next()) |phdr| { 72 if (phdr.p_type == elf.PT_LOAD) { 73 const newSegment = try allocator.create(BinaryElfSegment); 74 75 newSegment.physicalAddress = if (phdr.p_paddr != 0) phdr.p_paddr else phdr.p_vaddr; 76 newSegment.virtualAddress = phdr.p_vaddr; 77 newSegment.fileSize = @intCast(usize, phdr.p_filesz); 78 newSegment.elfOffset = phdr.p_offset; 79 newSegment.binaryOffset = 0; 80 newSegment.firstSection = null; 81 82 for (self.sections.items) |section| { 83 if (sectionWithinSegment(section, phdr)) { 84 if (section.segment) |sectionSegment| { 85 if (sectionSegment.elfOffset > newSegment.elfOffset) { 86 section.segment = newSegment; 87 } 88 } else { 89 section.segment = newSegment; 90 } 91 92 if (newSegment.firstSection == null) { 93 newSegment.firstSection = section; 94 } 95 } 96 } 97 98 try self.segments.append(newSegment); 99 } 100 } 101 102 sort.sort(*BinaryElfSegment, self.segments.items, {}, segmentSortCompare); 103 104 if (self.segments.items.len > 0) { 105 const firstSegment = self.segments.items[0]; 106 if (firstSegment.firstSection) |firstSection| { 107 const diff = firstSection.elfOffset - firstSegment.elfOffset; 108 109 firstSegment.elfOffset += diff; 110 firstSegment.fileSize += diff; 111 firstSegment.physicalAddress += diff; 112 113 const basePhysicalAddress = firstSegment.physicalAddress; 114 115 for (self.segments.items) |segment| { 116 segment.binaryOffset = segment.physicalAddress - basePhysicalAddress; 117 } 118 } 119 } 120 121 for (self.sections.items) |section| { 122 if (section.segment) |segment| { 123 section.binaryOffset = segment.binaryOffset + (section.elfOffset - segment.elfOffset); 124 } 125 } 126 127 sort.sort(*BinaryElfSection, self.sections.items, {}, sectionSortCompare); 128 129 return self; 130 } 131 132 fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.Elf64_Phdr) bool { 133 return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize); 134 } 135 136 fn sectionValidForOutput(shdr: anytype) bool { 137 return shdr.sh_size > 0 and shdr.sh_type != elf.SHT_NOBITS and 138 ((shdr.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC); 139 } 140 141 fn segmentSortCompare(context: void, left: *BinaryElfSegment, right: *BinaryElfSegment) bool { 142 if (left.physicalAddress < right.physicalAddress) { 143 return true; 144 } 145 if (left.physicalAddress > right.physicalAddress) { 146 return false; 147 } 148 return false; 149 } 150 151 fn sectionSortCompare(context: void, left: *BinaryElfSection, right: *BinaryElfSection) bool { 152 return left.binaryOffset < right.binaryOffset; 153 } 154 }; 155 156 fn writeBinaryElfSection(elf_file: File, out_file: File, section: *BinaryElfSection) !void { 157 try out_file.seekTo(section.binaryOffset); 158 159 try out_file.writeFileAll(elf_file, .{ 160 .in_offset = section.elfOffset, 161 .in_len = section.fileSize, 162 }); 163 } 164 165 fn emitRaw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !void { 166 var elf_file = try fs.cwd().openFile(elf_path, .{}); 167 defer elf_file.close(); 168 169 var out_file = try fs.cwd().createFile(raw_path, .{}); 170 defer out_file.close(); 171 172 var binary_elf_output = try BinaryElfOutput.parse(allocator, elf_file); 173 defer binary_elf_output.deinit(); 174 175 for (binary_elf_output.sections.items) |section| { 176 try writeBinaryElfSection(elf_file, out_file, section); 177 } 178 } 179 180 pub const InstallRawStep = struct { 181 step: Step, 182 builder: *Builder, 183 artifact: *LibExeObjStep, 184 dest_dir: InstallDir, 185 dest_filename: []const u8, 186 187 const Self = @This(); 188 189 pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) *Self { 190 const self = builder.allocator.create(Self) catch unreachable; 191 self.* = Self{ 192 .step = Step.init(.InstallRaw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make), 193 .builder = builder, 194 .artifact = artifact, 195 .dest_dir = switch (artifact.kind) { 196 .Obj => unreachable, 197 .Test => unreachable, 198 .Exe => .Bin, 199 .Lib => unreachable, 200 }, 201 .dest_filename = dest_filename, 202 }; 203 self.step.dependOn(&artifact.step); 204 205 builder.pushInstalledFile(self.dest_dir, dest_filename); 206 return self; 207 } 208 209 fn make(step: *Step) !void { 210 const self = @fieldParentPtr(Self, "step", step); 211 const builder = self.builder; 212 213 if (self.artifact.target.getObjectFormat() != .elf) { 214 warn("InstallRawStep only works with ELF format.\n", .{}); 215 return error.InvalidObjectFormat; 216 } 217 218 const full_src_path = self.artifact.getOutputPath(); 219 const full_dest_path = builder.getInstallPath(self.dest_dir, self.dest_filename); 220 221 fs.cwd().makePath(builder.getInstallPath(self.dest_dir, "")) catch unreachable; 222 try emitRaw(builder.allocator, full_src_path, full_dest_path); 223 } 224 }; 225 226 test "" { 227 std.testing.refAllDecls(InstallRawStep); 228 }