-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathbuild.zig
403 lines (371 loc) · 13.4 KB
/
build.zig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
const std = @import("std");
const NativeTargetInfo = std.zig.system.NativeTargetInfo;
fn shouldStrip(mode: std.builtin.OptimizeMode) bool {
return switch (mode) {
.Debug, .ReleaseSafe => false,
.ReleaseFast, .ReleaseSmall => true,
};
}
const SharedOptions = struct {
const InstructionCount = enum {
kernel,
user,
all,
none,
};
instruction_count: InstructionCount,
};
fn buildKernel(
b: *std.Build,
std_target: std.Build.ResolvedTarget,
std_optimize: std.builtin.OptimizeMode,
shared_options: SharedOptions,
) void {
// Custom x86_64 freestanding target for the kernel
const target = std.Target.Query.parse(.{
.arch_os_abi = "x86_64-freestanding-none",
.cpu_features = "x86_64-mmx-sse-sse2+soft_float",
}) catch unreachable;
// Kernel executable build step
const exe = b.addExecutable(.{
.name = "kernel",
.root_source_file = b.path("kernel/src/main.zig"),
.target = std.Build.resolveTargetQuery(b, target),
.optimize = std_optimize,
.code_model = .kernel,
.single_threaded = true,
.strip = shouldStrip(std_optimize),
});
exe.setLinkerScript(b.path("kernel/linker.ld"));
exe.entry = .{ .symbol_name = "kmain" };
exe.root_module.red_zone = false;
// zig build options
const enable_guest_output = b.option(
bool,
"enable-guest-output",
"Enable guest output to stdout and stderr. Default is disabled.",
) orelse false;
// Kernel build options
const build_options = b.addOptions();
build_options.addOption(
SharedOptions.InstructionCount,
"instruction_count",
shared_options.instruction_count,
);
build_options.addOption(bool, "enable_guest_output", enable_guest_output);
exe.root_module.addOptions("build_options", build_options);
b.installArtifact(exe);
// Unit tests
const exe_tests = b.addTest(.{
.root_source_file = b.path("kernel/src/main.zig"),
.target = std_target,
.optimize = std_optimize,
});
exe_tests.root_module.addOptions("build_options", build_options);
const run_exe_tests = b.addRunArtifact(exe_tests);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_exe_tests.step);
// Format step
const fmt_step = b.addFmt(.{ .paths = &.{"kernel/src"} });
exe.step.dependOn(&fmt_step.step);
}
fn addHypervisorOptions(
b: *std.Build,
exe: *std.Build.Step.Compile,
shared_options: SharedOptions,
) void {
// Mutations
const enable_mutations = b.option(
bool,
"enable-mutations",
"Enable mutations. If disabled, inputs will be chosen from the initial " ++
"corpus but they won't be mutated. Default is enabled.",
) orelse true;
if (enable_mutations) {
exe.defineCMacro("ENABLE_MUTATIONS", null);
}
// Coverage
const Coverage = enum {
breakpoints,
intelpt,
none,
};
const coverage = b.option(
Coverage,
"coverage",
"Type of code-coverage used. Breakpoints provide basic block coverage " ++
"only the first time the block is executed, while Intel PT provides " ++
"edge coverage for every run. Default is breakpoints.",
) orelse .breakpoints;
switch (coverage) {
.breakpoints => exe.defineCMacro("ENABLE_COVERAGE_BREAKPOINTS", null),
.intelpt => {
exe.defineCMacro("ENABLE_COVERAGE_INTEL_PT", null);
exe.linkSystemLibrary("xdc");
},
.none => {},
}
// Bitmap size
const bitmap_size_str = b.option(
[]const u8,
"bitmap-size",
"Bitmap size used for Intel PT coverage. Default is 64K.",
);
// Check if option was given but coverage type is not Intel PT
if (bitmap_size_str != null and coverage != .intelpt) {
std.log.warn("option bitmap-size was specified but coverage type is {s}, ignoring", .{@tagName(coverage)});
}
if (coverage == .intelpt) {
// Parse the string
const size_str = bitmap_size_str orelse "64K";
const number_len = std.mem.indexOfAny(u8, size_str, "KM") orelse size_str.len;
const number = std.fmt.parseInt(usize, size_str[0..number_len], 0) catch @panic("error parsing bitmap-size");
const size = if (number_len < size_str.len) switch (size_str[number_len]) {
'K' => number * 1024,
'M' => number * 1024 * 1024,
else => unreachable,
} else number;
const size_str_parsed = std.fmt.allocPrint(b.allocator, "{}", .{size}) catch unreachable;
defer b.allocator.free(size_str_parsed);
exe.defineCMacro("COVERAGE_BITMAP_SIZE", size_str_parsed);
}
// Dirty log ring
const kvm_dirty_log_ring_version_required = std.SemanticVersion{ .major = 5, .minor = 11, .patch = 0 };
const enable_kvm_dirty_log_ring = b.option(
bool,
"enable-kvm-dirty-log-ring",
std.fmt.comptimePrint(
"Enable KVM dirty log ring, available from Linux {}. If disabled, " ++
"the usual bitmap is used. Default is disabled.",
.{kvm_dirty_log_ring_version_required},
),
) orelse false;
if (enable_kvm_dirty_log_ring) {
const linux_version_range = exe.rootModuleTarget().os.version_range.linux;
const version_ok = linux_version_range.isAtLeast(kvm_dirty_log_ring_version_required) orelse return;
if (!version_ok) {
std.log.warn(
"Option enable_kvm_dirty_log_ring requires kernel >= {}, " ++
"current is {}. Compilation will continue but it will probably " ++
"fail at runtime.",
.{ kvm_dirty_log_ring_version_required, linux_version_range.range.min },
);
}
exe.defineCMacro("ENABLE_KVM_DIRTY_LOG_RING", null);
}
if (shared_options.instruction_count != .none) {
exe.defineCMacro("ENABLE_INSTRUCTION_COUNT", null);
}
}
fn buildHypervisor(
b: *std.Build,
std_target: std.Build.ResolvedTarget,
std_optimize: std.builtin.OptimizeMode,
shared_options: SharedOptions,
) void {
const exe = b.addExecutable(.{
.name = "kvm-fuzz",
.target = std_target,
.optimize = std_optimize,
.strip = shouldStrip(std_optimize),
});
addHypervisorOptions(b, exe, shared_options);
exe.addIncludePath(b.path("hypervisor/include"));
exe.addCSourceFiles(.{
.root = b.path("hypervisor/src"),
.files = &.{
"args.cpp",
"corpus.cpp",
"elf_debug.cpp",
"elf_parser.cpp",
"elfs.cpp",
"files.cpp",
"hypercalls.cpp",
"main.cpp",
"mutator.cpp",
"mmu.cpp",
"page_walker.cpp",
"tracing.cpp",
"utils.cpp",
"vm.cpp",
},
.flags = &.{
"-std=c++11",
"-pthread",
"-fno-exceptions",
"-Wall",
},
});
exe.linkLibC();
exe.linkLibCpp();
exe.linkSystemLibrary("dwarf");
exe.linkSystemLibrary("elf");
exe.linkSystemLibrary("crypto");
b.installArtifact(exe);
}
fn buildSyscallsTests(b: *std.Build, std_target: std.Build.ResolvedTarget, std_optimize: std.builtin.OptimizeMode) void {
// It would be great to link this binary statically. We'd need to link with
// musl instead of glibc, but musl breaks tests. For example, musl implements
// brk as `return -ENOMEM;` (https://www.openwall.com/lists/musl/2013/12/21/1).
// Maybe the best would be that tests call syscalls directly, instead of
// relying on the linked libc.
// Use baseline cpu model so it doesn't generate AVX512 or other not supported
// x86 extensions
// var target = std_target.query;
// target.cpu_model = .baseline;
// target.setGnuLibCVersion(2, 34, 0); // workaround for libc.so.6 not being a symlink in ubuntu 21+
const exe = b.addExecutable(.{
.name = "syscalls_tests",
.target = std_target, // TODO change with target
.optimize = std_optimize,
.strip = true,
});
exe.addIncludePath(b.path("tests"));
exe.addCSourceFiles(.{
.root = b.path("tests/syscalls"),
.files = &.{
"brk.cpp",
"dup.cpp",
"fcntl.cpp",
"files.cpp",
"fork.cpp",
"getcwd.cpp",
"main.cpp",
"misc.cpp",
"mmap.cpp",
"readlink.cpp",
"safe_mem.cpp",
"sched.cpp",
"socket.cpp",
"stdin.cpp",
"thread_local.cpp",
"uname.cpp",
},
.flags = &.{
"-std=c++11",
"-Wall",
},
});
exe.linkLibC();
exe.linkLibCpp();
const install = b.addInstallArtifact(exe, .{});
const build_step = b.step("syscalls_tests", "Build syscalls tests");
build_step.dependOn(&install.step);
}
fn buildHypervisorTests(b: *std.Build, std_target: std.Build.ResolvedTarget, std_optimize: std.builtin.OptimizeMode) void {
const exe = b.addExecutable(.{
.name = "hypervisor_tests",
.target = std_target,
.optimize = std_optimize,
});
exe.addIncludePath(b.path("tests"));
exe.addIncludePath(b.path("hypervisor/include"));
exe.addCSourceFiles(.{
.files = &.{
"hypervisor/src/elf_debug.cpp",
"hypervisor/src/elf_parser.cpp",
"hypervisor/src/elfs.cpp",
"hypervisor/src/files.cpp",
"hypervisor/src/hypercalls.cpp",
"hypervisor/src/mmu.cpp",
"hypervisor/src/page_walker.cpp",
"hypervisor/src/tracing.cpp",
"hypervisor/src/utils.cpp",
"hypervisor/src/vm.cpp",
"tests/hypervisor/files.cpp",
"tests/hypervisor/hooks.cpp",
"tests/hypervisor/inst_count.cpp",
"tests/hypervisor/main.cpp",
},
.flags = &.{
"-std=c++11",
},
});
exe.defineCMacro("ENABLE_INSTRUCTION_COUNT", null);
exe.linkLibC();
exe.linkLibCpp();
exe.linkSystemLibrary("dwarf");
exe.linkSystemLibrary("elf");
exe.linkSystemLibrary("crypto");
const install = b.addInstallArtifact(exe, .{});
const build_step = b.step("hypervisor_tests", "Build hypervisor tests");
build_step.dependOn(&install.step);
// Binaries needed for the tests
const test_hooks_exe = b.addExecutable(.{
.name = "test_hooks",
.target = std_target,
});
test_hooks_exe.addAssemblyFile(b.path("tests/hypervisor/binaries/hooks.s"));
const test_hooks_install = b.addInstallArtifact(test_hooks_exe, .{});
install.step.dependOn(&test_hooks_install.step);
const test_files_exe = b.addExecutable(.{
.name = "test_files",
.target = std_target,
});
test_files_exe.addCSourceFile(.{ .file = b.path("tests/hypervisor/binaries/files.c") });
test_files_exe.linkLibC();
const test_files_install = b.addInstallArtifact(test_files_exe, .{});
install.step.dependOn(&test_files_install.step);
}
fn buildExperiments(b: *std.Build, std_target: std.Build.ResolvedTarget, std_optimize: std.builtin.OptimizeMode) void {
const exe = b.addExecutable(.{
.name = "resets_exp",
.target = std_target,
.optimize = std_optimize,
});
exe.addIncludePath(b.path("hypervisor/include"));
exe.addCSourceFiles(.{
.root = b.path("hypervisor"),
.files = &.{
"experiments/resets/resets_exp.cpp",
"src/elf_debug.cpp",
"src/elf_parser.cpp",
"src/elfs.cpp",
"src/files.cpp",
"src/hypercalls.cpp",
"src/mmu.cpp",
"src/page_walker.cpp",
"src/utils.cpp",
"src/tracing.cpp",
"src/vm.cpp",
},
.flags = &.{
"-std=c++11",
},
});
exe.defineCMacro("ENABLE_INSTRUCTION_COUNT", null);
exe.linkLibC();
exe.linkLibCpp();
exe.linkSystemLibrary("dwarf");
exe.linkSystemLibrary("elf");
exe.linkSystemLibrary("crypto");
const install = b.addInstallArtifact(exe, .{});
const build_step = b.step("experiments", "Build experiments");
build_step.dependOn(&install.step);
const resets_test_exe = b.addExecutable(.{
.name = "resets_test",
.root_source_file = b.path("hypervisor/experiments/resets/resets_test.c"),
.target = std_target,
});
resets_test_exe.linkLibC();
const resets_test_install = b.addInstallArtifact(resets_test_exe, .{});
install.step.dependOn(&resets_test_install.step);
}
pub fn build(b: *std.Build) void {
const std_target = b.standardTargetOptions(.{});
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const std_optimize = b.standardOptimizeOption(.{});
const shared_options = SharedOptions{
.instruction_count = b.option(
SharedOptions.InstructionCount,
"instruction-count",
"Instruction count mode. Default is user.",
) orelse .user,
};
buildKernel(b, std_target, std_optimize, shared_options);
buildHypervisor(b, std_target, std_optimize, shared_options);
buildSyscallsTests(b, std_target, std_optimize);
buildHypervisorTests(b, std_target, std_optimize);
buildExperiments(b, std_target, std_optimize);
}