zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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 }