Skip to content

Commit faa08c5

Browse files
committed
Add InstallRawStep to Zig build system that does a similar job to llvm-objcopy. To use it, do
```zig exe.installRaw("kernel.bin"); ``` where exe is a LibExeObjStep Part of #2826
1 parent 4f2652d commit faa08c5

File tree

8 files changed

+391
-86
lines changed

8 files changed

+391
-86
lines changed

lib/std/build.zig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub const TranslateCStep = @import("build/translate_c.zig").TranslateCStep;
2121
pub const WriteFileStep = @import("build/write_file.zig").WriteFileStep;
2222
pub const RunStep = @import("build/run.zig").RunStep;
2323
pub const CheckFileStep = @import("build/check_file.zig").CheckFileStep;
24+
pub const InstallRawStep = @import("build/emit_raw.zig").InstallRawStep;
2425

2526
pub const Builder = struct {
2627
install_tls: TopLevelStep,
@@ -824,6 +825,10 @@ pub const Builder = struct {
824825
self.getInstallStep().dependOn(&self.addInstallFileWithDir(src_path, .Lib, dest_rel_path).step);
825826
}
826827

828+
pub fn installRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) void {
829+
self.getInstallStep().dependOn(&self.addInstallRaw(artifact, dest_filename).step);
830+
}
831+
827832
///`dest_rel_path` is relative to install prefix path
828833
pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
829834
return self.addInstallFileWithDir(src_path, .Prefix, dest_rel_path);
@@ -839,6 +844,10 @@ pub const Builder = struct {
839844
return self.addInstallFileWithDir(src_path, .Lib, dest_rel_path);
840845
}
841846

847+
pub fn addInstallRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) *InstallRawStep {
848+
return InstallRawStep.create(self, artifact, dest_filename);
849+
}
850+
842851
pub fn addInstallFileWithDir(
843852
self: *Builder,
844853
src_path: []const u8,
@@ -1406,6 +1415,10 @@ pub const LibExeObjStep = struct {
14061415
self.builder.installArtifact(self);
14071416
}
14081417

1418+
pub fn installRaw(self: *LibExeObjStep, dest_filename: [] const u8) void {
1419+
self.builder.installRaw(self, dest_filename);
1420+
}
1421+
14091422
/// Creates a `RunStep` with an executable built with `addExecutable`.
14101423
/// Add command line arguments with `addArg`.
14111424
pub fn run(exe: *LibExeObjStep) *RunStep {

lib/std/build/emit_raw.zig

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
const std = @import("std");
2+
3+
const Allocator = std.mem.Allocator;
4+
const ArenaAllocator = std.heap.ArenaAllocator;
5+
const ArrayList = std.ArrayList;
6+
const Builder = std.build.Builder;
7+
const File = std.fs.File;
8+
const InstallDir = std.build.InstallDir;
9+
const LibExeObjStep = std.build.LibExeObjStep;
10+
const Step = std.build.Step;
11+
const elf = std.elf;
12+
const fs = std.fs;
13+
const io = std.io;
14+
const sort = std.sort;
15+
const warn = std.debug.warn;
16+
17+
const BinOutStream = io.OutStream(anyerror);
18+
const BinSeekStream = io.SeekableStream(anyerror, anyerror);
19+
const ElfSeekStream = io.SeekableStream(anyerror, anyerror);
20+
const ElfInStream = io.InStream(anyerror);
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 init(allocator: *Allocator) Self {
45+
return Self{
46+
.segments = ArrayList(*BinaryElfSegment).init(allocator),
47+
.sections = ArrayList(*BinaryElfSection).init(allocator),
48+
};
49+
}
50+
51+
pub fn deinit(self: *Self) void {
52+
self.sections.deinit();
53+
self.segments.deinit();
54+
}
55+
56+
pub fn parseElf(self: *Self, elfFile: elf.Elf) !void {
57+
const allocator = self.segments.allocator;
58+
59+
for (elfFile.section_headers) |section, i| {
60+
if (sectionValidForOutput(section)) {
61+
const newSection = try allocator.create(BinaryElfSection);
62+
63+
newSection.binaryOffset = 0;
64+
newSection.elfOffset = section.sh_offset;
65+
newSection.fileSize = @intCast(usize, section.sh_size);
66+
newSection.segment = null;
67+
68+
try self.sections.append(newSection);
69+
}
70+
}
71+
72+
for (elfFile.program_headers) |programHeader, i| {
73+
if (programHeader.p_type == elf.PT_LOAD) {
74+
const newSegment = try allocator.create(BinaryElfSegment);
75+
76+
newSegment.physicalAddress = if (programHeader.p_paddr != 0) programHeader.p_paddr else programHeader.p_vaddr;
77+
newSegment.virtualAddress = programHeader.p_vaddr;
78+
newSegment.fileSize = @intCast(usize, programHeader.p_filesz);
79+
newSegment.elfOffset = programHeader.p_offset;
80+
newSegment.binaryOffset = 0;
81+
newSegment.firstSection = null;
82+
83+
for (self.sections.toSlice()) |section| {
84+
if (sectionWithinSegment(section, programHeader)) {
85+
if (section.segment) |sectionSegment| {
86+
if (sectionSegment.elfOffset > newSegment.elfOffset) {
87+
section.segment = newSegment;
88+
}
89+
} else {
90+
section.segment = newSegment;
91+
}
92+
93+
if (newSegment.firstSection == null) {
94+
newSegment.firstSection = section;
95+
}
96+
}
97+
}
98+
99+
try self.segments.append(newSegment);
100+
}
101+
}
102+
103+
sort.sort(*BinaryElfSegment, self.segments.toSlice(), segmentSortCompare);
104+
105+
if (self.segments.len > 0) {
106+
const firstSegment = self.segments.at(0);
107+
if (firstSegment.firstSection) |firstSection| {
108+
const diff = firstSection.elfOffset - firstSegment.elfOffset;
109+
110+
firstSegment.elfOffset += diff;
111+
firstSegment.fileSize += diff;
112+
firstSegment.physicalAddress += diff;
113+
114+
const basePhysicalAddress = firstSegment.physicalAddress;
115+
116+
for (self.segments.toSlice()) |segment| {
117+
segment.binaryOffset = segment.physicalAddress - basePhysicalAddress;
118+
}
119+
}
120+
}
121+
122+
for (self.sections.toSlice()) |section| {
123+
if (section.segment) |segment| {
124+
section.binaryOffset = segment.binaryOffset + (section.elfOffset - segment.elfOffset);
125+
}
126+
}
127+
128+
sort.sort(*BinaryElfSection, self.sections.toSlice(), sectionSortCompare);
129+
}
130+
131+
fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.ProgramHeader) bool {
132+
return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize);
133+
}
134+
135+
fn sectionValidForOutput(section: elf.SectionHeader) bool {
136+
return section.sh_size > 0 and section.sh_type != elf.SHT_NOBITS and ((section.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC);
137+
}
138+
139+
fn segmentSortCompare(left: *BinaryElfSegment, right: *BinaryElfSegment) bool {
140+
if (left.physicalAddress < right.physicalAddress) {
141+
return true;
142+
}
143+
if (left.physicalAddress > right.physicalAddress) {
144+
return false;
145+
}
146+
return false;
147+
}
148+
149+
fn sectionSortCompare(left: *BinaryElfSection, right: *BinaryElfSection) bool {
150+
return left.binaryOffset < right.binaryOffset;
151+
}
152+
};
153+
154+
const WriteContext = struct {
155+
inStream: *ElfInStream,
156+
inSeekStream: *ElfSeekStream,
157+
outStream: *BinOutStream,
158+
outSeekStream: *BinSeekStream,
159+
};
160+
161+
fn writeBinaryElfSection(allocator: *Allocator, context: WriteContext, section: *BinaryElfSection) !void {
162+
var readBuffer = try allocator.alloc(u8, section.fileSize);
163+
defer allocator.free(readBuffer);
164+
165+
try context.inSeekStream.seekTo(section.elfOffset);
166+
_ = try context.inStream.read(readBuffer);
167+
168+
try context.outSeekStream.seekTo(section.binaryOffset);
169+
try context.outStream.write(readBuffer);
170+
}
171+
172+
fn emit_raw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !void {
173+
var arenaAlloc = ArenaAllocator.init(allocator);
174+
errdefer arenaAlloc.deinit();
175+
var arena_allocator = &arenaAlloc.allocator;
176+
177+
const currentDir = fs.cwd();
178+
179+
var file = try currentDir.openFile(elf_path, File.OpenFlags{});
180+
defer file.close();
181+
182+
var fileInStream = file.inStream();
183+
var fileSeekStream = file.seekableStream();
184+
185+
var elfFile = try elf.Elf.openStream(allocator, @ptrCast(*ElfSeekStream, &fileSeekStream.stream), @ptrCast(*ElfInStream, &fileInStream.stream));
186+
defer elfFile.close();
187+
188+
var outFile = try currentDir.createFile(raw_path, File.CreateFlags{});
189+
defer outFile.close();
190+
191+
var outFileOutStream = outFile.outStream();
192+
var outFileSeekStream = outFile.seekableStream();
193+
194+
const writeContext = WriteContext{
195+
.inStream = @ptrCast(*ElfInStream, &fileInStream.stream),
196+
.inSeekStream = @ptrCast(*ElfSeekStream, &fileSeekStream.stream),
197+
.outStream = @ptrCast(*BinOutStream, &outFileOutStream.stream),
198+
.outSeekStream = @ptrCast(*BinSeekStream, &outFileSeekStream.stream),
199+
};
200+
201+
var binaryElfOutput = BinaryElfOutput.init(arena_allocator);
202+
defer binaryElfOutput.deinit();
203+
204+
try binaryElfOutput.parseElf(elfFile);
205+
206+
for (binaryElfOutput.sections.toSlice()) |section| {
207+
try writeBinaryElfSection(allocator, writeContext, section);
208+
}
209+
}
210+
211+
pub const InstallRawStep = struct {
212+
step: Step,
213+
builder: *Builder,
214+
artifact: *LibExeObjStep,
215+
dest_dir: InstallDir,
216+
dest_filename: [] const u8,
217+
218+
const Self = @This();
219+
220+
pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: [] const u8) *Self {
221+
const self = builder.allocator.create(Self) catch unreachable;
222+
self.* = Self{
223+
.step = Step.init(builder.fmt("install raw binary {}", .{artifact.step.name}), builder.allocator, make),
224+
.builder = builder,
225+
.artifact = artifact,
226+
.dest_dir = switch (artifact.kind) {
227+
.Obj => unreachable,
228+
.Test => unreachable,
229+
.Exe => .Bin,
230+
.Lib => unreachable,
231+
},
232+
.dest_filename = dest_filename,
233+
};
234+
self.step.dependOn(&artifact.step);
235+
236+
builder.pushInstalledFile(self.dest_dir, dest_filename);
237+
return self;
238+
}
239+
240+
fn make(step: *Step) !void {
241+
const self = @fieldParentPtr(Self, "step", step);
242+
const builder = self.builder;
243+
244+
if (self.artifact.target.getObjectFormat() != .elf) {
245+
warn("InstallRawStep only works with ELF format.\n", .{});
246+
return error.InvalidObjectFormat;
247+
}
248+
249+
const full_src_path = self.artifact.getOutputPath();
250+
const full_dest_path = builder.getInstallPath(self.dest_dir, self.dest_filename);
251+
252+
fs.makePath(builder.allocator, builder.getInstallPath(self.dest_dir, "")) catch unreachable;
253+
try emit_raw(builder.allocator, full_src_path, full_dest_path);
254+
}
255+
};

lib/std/debug.zig

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -946,8 +946,8 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {
946946
fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Section {
947947
const elf_header = (try elf_file.findSection(name)) orelse return null;
948948
return DwarfInfo.Section{
949-
.offset = elf_header.offset,
950-
.size = elf_header.size,
949+
.offset = elf_header.sh_offset,
950+
.size = elf_header.sh_size,
951951
};
952952
}
953953

@@ -987,12 +987,12 @@ pub fn openElfDebugInfo(
987987

988988
var di = DwarfInfo{
989989
.endian = efile.endian,
990-
.debug_info = (data[@intCast(usize, debug_info.offset)..@intCast(usize, debug_info.offset + debug_info.size)]),
991-
.debug_abbrev = (data[@intCast(usize, debug_abbrev.offset)..@intCast(usize, debug_abbrev.offset + debug_abbrev.size)]),
992-
.debug_str = (data[@intCast(usize, debug_str.offset)..@intCast(usize, debug_str.offset + debug_str.size)]),
993-
.debug_line = (data[@intCast(usize, debug_line.offset)..@intCast(usize, debug_line.offset + debug_line.size)]),
990+
.debug_info = (data[@intCast(usize, debug_info.sh_offset)..@intCast(usize, debug_info.sh_offset + debug_info.sh_size)]),
991+
.debug_abbrev = (data[@intCast(usize, debug_abbrev.sh_offset)..@intCast(usize, debug_abbrev.sh_offset + debug_abbrev.sh_size)]),
992+
.debug_str = (data[@intCast(usize, debug_str.sh_offset)..@intCast(usize, debug_str.sh_offset + debug_str.sh_size)]),
993+
.debug_line = (data[@intCast(usize, debug_line.sh_offset)..@intCast(usize, debug_line.sh_offset + debug_line.sh_size)]),
994994
.debug_ranges = if (opt_debug_ranges) |debug_ranges|
995-
data[@intCast(usize, debug_ranges.offset)..@intCast(usize, debug_ranges.offset + debug_ranges.size)]
995+
data[@intCast(usize, debug_ranges.sh_offset)..@intCast(usize, debug_ranges.sh_offset + debug_ranges.sh_size)]
996996
else
997997
null,
998998
};

0 commit comments

Comments
 (0)