Skip to content

Commit

Permalink
Add InstallRawStep to Zig build system that does a similar job to llv…
Browse files Browse the repository at this point in the history
…m-objcopy. To use it, do 'exe.installRaw("kernel.bin");' where exe is a LibExeObjStep

Part of #2826
  • Loading branch information
mlarouche authored and andrewrk committed Feb 3, 2020
1 parent 958f00f commit 9b11e5e
Show file tree
Hide file tree
Showing 8 changed files with 391 additions and 86 deletions.
13 changes: 13 additions & 0 deletions lib/std/build.zig
Expand Up @@ -21,6 +21,7 @@ pub const TranslateCStep = @import("build/translate_c.zig").TranslateCStep;
pub const WriteFileStep = @import("build/write_file.zig").WriteFileStep;
pub const RunStep = @import("build/run.zig").RunStep;
pub const CheckFileStep = @import("build/check_file.zig").CheckFileStep;
pub const InstallRawStep = @import("build/emit_raw.zig").InstallRawStep;

pub const Builder = struct {
install_tls: TopLevelStep,
Expand Down Expand Up @@ -824,6 +825,10 @@ pub const Builder = struct {
self.getInstallStep().dependOn(&self.addInstallFileWithDir(src_path, .Lib, dest_rel_path).step);
}

pub fn installRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) void {
self.getInstallStep().dependOn(&self.addInstallRaw(artifact, dest_filename).step);
}

///`dest_rel_path` is relative to install prefix path
pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
return self.addInstallFileWithDir(src_path, .Prefix, dest_rel_path);
Expand All @@ -839,6 +844,10 @@ pub const Builder = struct {
return self.addInstallFileWithDir(src_path, .Lib, dest_rel_path);
}

pub fn addInstallRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) *InstallRawStep {
return InstallRawStep.create(self, artifact, dest_filename);
}

pub fn addInstallFileWithDir(
self: *Builder,
src_path: []const u8,
Expand Down Expand Up @@ -1407,6 +1416,10 @@ pub const LibExeObjStep = struct {
self.builder.installArtifact(self);
}

pub fn installRaw(self: *LibExeObjStep, dest_filename: [] const u8) void {
self.builder.installRaw(self, dest_filename);
}

/// Creates a `RunStep` with an executable built with `addExecutable`.
/// Add command line arguments with `addArg`.
pub fn run(exe: *LibExeObjStep) *RunStep {
Expand Down
255 changes: 255 additions & 0 deletions lib/std/build/emit_raw.zig
@@ -0,0 +1,255 @@
const std = @import("std");

const Allocator = std.mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator;
const ArrayList = std.ArrayList;
const Builder = std.build.Builder;
const File = std.fs.File;
const InstallDir = std.build.InstallDir;
const LibExeObjStep = std.build.LibExeObjStep;
const Step = std.build.Step;
const elf = std.elf;
const fs = std.fs;
const io = std.io;
const sort = std.sort;
const warn = std.debug.warn;

const BinOutStream = io.OutStream(anyerror);
const BinSeekStream = io.SeekableStream(anyerror, anyerror);
const ElfSeekStream = io.SeekableStream(anyerror, anyerror);
const ElfInStream = io.InStream(anyerror);

const BinaryElfSection = struct {
elfOffset: u64,
binaryOffset: u64,
fileSize: usize,
segment: ?*BinaryElfSegment,
};

const BinaryElfSegment = struct {
physicalAddress: u64,
virtualAddress: u64,
elfOffset: u64,
binaryOffset: u64,
fileSize: usize,
firstSection: ?*BinaryElfSection,
};

const BinaryElfOutput = struct {
segments: ArrayList(*BinaryElfSegment),
sections: ArrayList(*BinaryElfSection),

const Self = @This();

pub fn init(allocator: *Allocator) Self {
return Self{
.segments = ArrayList(*BinaryElfSegment).init(allocator),
.sections = ArrayList(*BinaryElfSection).init(allocator),
};
}

pub fn deinit(self: *Self) void {
self.sections.deinit();
self.segments.deinit();
}

pub fn parseElf(self: *Self, elfFile: elf.Elf) !void {
const allocator = self.segments.allocator;

for (elfFile.section_headers) |section, i| {
if (sectionValidForOutput(section)) {
const newSection = try allocator.create(BinaryElfSection);

newSection.binaryOffset = 0;
newSection.elfOffset = section.sh_offset;
newSection.fileSize = @intCast(usize, section.sh_size);
newSection.segment = null;

try self.sections.append(newSection);
}
}

for (elfFile.program_headers) |programHeader, i| {
if (programHeader.p_type == elf.PT_LOAD) {
const newSegment = try allocator.create(BinaryElfSegment);

newSegment.physicalAddress = if (programHeader.p_paddr != 0) programHeader.p_paddr else programHeader.p_vaddr;
newSegment.virtualAddress = programHeader.p_vaddr;
newSegment.fileSize = @intCast(usize, programHeader.p_filesz);
newSegment.elfOffset = programHeader.p_offset;
newSegment.binaryOffset = 0;
newSegment.firstSection = null;

for (self.sections.toSlice()) |section| {
if (sectionWithinSegment(section, programHeader)) {
if (section.segment) |sectionSegment| {
if (sectionSegment.elfOffset > newSegment.elfOffset) {
section.segment = newSegment;
}
} else {
section.segment = newSegment;
}

if (newSegment.firstSection == null) {
newSegment.firstSection = section;
}
}
}

try self.segments.append(newSegment);
}
}

sort.sort(*BinaryElfSegment, self.segments.toSlice(), segmentSortCompare);

if (self.segments.len > 0) {
const firstSegment = self.segments.at(0);
if (firstSegment.firstSection) |firstSection| {
const diff = firstSection.elfOffset - firstSegment.elfOffset;

firstSegment.elfOffset += diff;
firstSegment.fileSize += diff;
firstSegment.physicalAddress += diff;

const basePhysicalAddress = firstSegment.physicalAddress;

for (self.segments.toSlice()) |segment| {
segment.binaryOffset = segment.physicalAddress - basePhysicalAddress;
}
}
}

for (self.sections.toSlice()) |section| {
if (section.segment) |segment| {
section.binaryOffset = segment.binaryOffset + (section.elfOffset - segment.elfOffset);
}
}

sort.sort(*BinaryElfSection, self.sections.toSlice(), sectionSortCompare);
}

fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.ProgramHeader) bool {
return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize);
}

fn sectionValidForOutput(section: elf.SectionHeader) bool {
return section.sh_size > 0 and section.sh_type != elf.SHT_NOBITS and ((section.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC);
}

fn segmentSortCompare(left: *BinaryElfSegment, right: *BinaryElfSegment) bool {
if (left.physicalAddress < right.physicalAddress) {
return true;
}
if (left.physicalAddress > right.physicalAddress) {
return false;
}
return false;
}

fn sectionSortCompare(left: *BinaryElfSection, right: *BinaryElfSection) bool {
return left.binaryOffset < right.binaryOffset;
}
};

const WriteContext = struct {
inStream: *ElfInStream,
inSeekStream: *ElfSeekStream,
outStream: *BinOutStream,
outSeekStream: *BinSeekStream,
};

fn writeBinaryElfSection(allocator: *Allocator, context: WriteContext, section: *BinaryElfSection) !void {
var readBuffer = try allocator.alloc(u8, section.fileSize);
defer allocator.free(readBuffer);

try context.inSeekStream.seekTo(section.elfOffset);
_ = try context.inStream.read(readBuffer);

try context.outSeekStream.seekTo(section.binaryOffset);
try context.outStream.write(readBuffer);
}

fn emit_raw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !void {
var arenaAlloc = ArenaAllocator.init(allocator);
errdefer arenaAlloc.deinit();
var arena_allocator = &arenaAlloc.allocator;

const currentDir = fs.cwd();

var file = try currentDir.openFile(elf_path, File.OpenFlags{});
defer file.close();

var fileInStream = file.inStream();
var fileSeekStream = file.seekableStream();

var elfFile = try elf.Elf.openStream(allocator, @ptrCast(*ElfSeekStream, &fileSeekStream.stream), @ptrCast(*ElfInStream, &fileInStream.stream));
defer elfFile.close();

var outFile = try currentDir.createFile(raw_path, File.CreateFlags{});
defer outFile.close();

var outFileOutStream = outFile.outStream();
var outFileSeekStream = outFile.seekableStream();

const writeContext = WriteContext{
.inStream = @ptrCast(*ElfInStream, &fileInStream.stream),
.inSeekStream = @ptrCast(*ElfSeekStream, &fileSeekStream.stream),
.outStream = @ptrCast(*BinOutStream, &outFileOutStream.stream),
.outSeekStream = @ptrCast(*BinSeekStream, &outFileSeekStream.stream),
};

var binaryElfOutput = BinaryElfOutput.init(arena_allocator);
defer binaryElfOutput.deinit();

try binaryElfOutput.parseElf(elfFile);

for (binaryElfOutput.sections.toSlice()) |section| {
try writeBinaryElfSection(allocator, writeContext, section);
}
}

pub const InstallRawStep = struct {
step: Step,
builder: *Builder,
artifact: *LibExeObjStep,
dest_dir: InstallDir,
dest_filename: [] const u8,

const Self = @This();

pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: [] const u8) *Self {
const self = builder.allocator.create(Self) catch unreachable;
self.* = Self{
.step = Step.init(builder.fmt("install raw binary {}", .{artifact.step.name}), builder.allocator, make),
.builder = builder,
.artifact = artifact,
.dest_dir = switch (artifact.kind) {
.Obj => unreachable,
.Test => unreachable,
.Exe => .Bin,
.Lib => unreachable,
},
.dest_filename = dest_filename,
};
self.step.dependOn(&artifact.step);

builder.pushInstalledFile(self.dest_dir, dest_filename);
return self;
}

fn make(step: *Step) !void {
const self = @fieldParentPtr(Self, "step", step);
const builder = self.builder;

if (self.artifact.target.getObjectFormat() != .elf) {
warn("InstallRawStep only works with ELF format.\n", .{});
return error.InvalidObjectFormat;
}

const full_src_path = self.artifact.getOutputPath();
const full_dest_path = builder.getInstallPath(self.dest_dir, self.dest_filename);

fs.makePath(builder.allocator, builder.getInstallPath(self.dest_dir, "")) catch unreachable;
try emit_raw(builder.allocator, full_src_path, full_dest_path);
}
};
14 changes: 7 additions & 7 deletions lib/std/debug.zig
Expand Up @@ -946,8 +946,8 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {
fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Section {
const elf_header = (try elf_file.findSection(name)) orelse return null;
return DwarfInfo.Section{
.offset = elf_header.offset,
.size = elf_header.size,
.offset = elf_header.sh_offset,
.size = elf_header.sh_size,
};
}

Expand Down Expand Up @@ -987,12 +987,12 @@ pub fn openElfDebugInfo(

var di = DwarfInfo{
.endian = efile.endian,
.debug_info = (data[@intCast(usize, debug_info.offset)..@intCast(usize, debug_info.offset + debug_info.size)]),
.debug_abbrev = (data[@intCast(usize, debug_abbrev.offset)..@intCast(usize, debug_abbrev.offset + debug_abbrev.size)]),
.debug_str = (data[@intCast(usize, debug_str.offset)..@intCast(usize, debug_str.offset + debug_str.size)]),
.debug_line = (data[@intCast(usize, debug_line.offset)..@intCast(usize, debug_line.offset + debug_line.size)]),
.debug_info = (data[@intCast(usize, debug_info.sh_offset)..@intCast(usize, debug_info.sh_offset + debug_info.sh_size)]),
.debug_abbrev = (data[@intCast(usize, debug_abbrev.sh_offset)..@intCast(usize, debug_abbrev.sh_offset + debug_abbrev.sh_size)]),
.debug_str = (data[@intCast(usize, debug_str.sh_offset)..@intCast(usize, debug_str.sh_offset + debug_str.sh_size)]),
.debug_line = (data[@intCast(usize, debug_line.sh_offset)..@intCast(usize, debug_line.sh_offset + debug_line.sh_size)]),
.debug_ranges = if (opt_debug_ranges) |debug_ranges|
data[@intCast(usize, debug_ranges.offset)..@intCast(usize, debug_ranges.offset + debug_ranges.size)]
data[@intCast(usize, debug_ranges.sh_offset)..@intCast(usize, debug_ranges.sh_offset + debug_ranges.sh_size)]
else
null,
};
Expand Down

0 comments on commit 9b11e5e

Please sign in to comment.