diff --git a/build.zig b/build.zig index 5f55726d05..7290d770db 100644 --- a/build.zig +++ b/build.zig @@ -305,7 +305,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { } dependOnLib(b, exe, ctx.llvm); - if (exe.target.getOs() == .linux) { + if (exe.target.getOsTag() == .linux) { try addCxxKnownPath(b, ctx, exe, "libstdc++.a", \\Unable to determine path to libstdc++.a \\On Fedora, install libstdc++-static and try again. diff --git a/doc/docgen.zig b/doc/docgen.zig index b429c93e65..5d7f2b7b38 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -1,5 +1,5 @@ -const builtin = @import("builtin"); const std = @import("std"); +const builtin = std.builtin; const io = std.io; const fs = std.fs; const process = std.process; @@ -10,8 +10,8 @@ const testing = std.testing; const max_doc_file_size = 10 * 1024 * 1024; -const exe_ext = @as(std.build.Target, std.build.Target.Native).exeFileExt(); -const obj_ext = @as(std.build.Target, std.build.Target.Native).oFileExt(); +const exe_ext = @as(std.zig.CrossTarget, .{}).exeFileExt(); +const obj_ext = @as(std.zig.CrossTarget, .{}).oFileExt(); const tmp_dir_name = "docgen_tmp"; const test_out_path = tmp_dir_name ++ fs.path.sep_str ++ "test" ++ exe_ext; @@ -521,7 +521,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {}", .{code_kind_str}); } - var mode = builtin.Mode.Debug; + var mode: builtin.Mode = .Debug; var link_objects = std.ArrayList([]const u8).init(allocator); defer link_objects.deinit(); var target_str: ?[]const u8 = null; @@ -533,9 +533,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { const end_code_tag = try eatToken(tokenizer, Token.Id.TagContent); const end_tag_name = tokenizer.buffer[end_code_tag.start..end_code_tag.end]; if (mem.eql(u8, end_tag_name, "code_release_fast")) { - mode = builtin.Mode.ReleaseFast; + mode = .ReleaseFast; } else if (mem.eql(u8, end_tag_name, "code_release_safe")) { - mode = builtin.Mode.ReleaseSafe; + mode = .ReleaseSafe; } else if (mem.eql(u8, end_tag_name, "code_link_object")) { _ = try eatToken(tokenizer, Token.Id.Separator); const obj_tok = try eatToken(tokenizer, Token.Id.TagContent); @@ -1001,30 +1001,30 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var for (toc.nodes) |node| { switch (node) { - Node.Content => |data| { + .Content => |data| { try out.write(data); }, - Node.Link => |info| { + .Link => |info| { if (!toc.urls.contains(info.url)) { return parseError(tokenizer, info.token, "url not found: {}", .{info.url}); } try out.print("{}", .{ info.url, info.name }); }, - Node.Nav => { + .Nav => { try out.write(toc.toc); }, - Node.Builtin => |tok| { + .Builtin => |tok| { try out.write("
"); try tokenizeAndPrintRaw(tokenizer, out, tok, builtin_code); try out.write(""); }, - Node.HeaderOpen => |info| { + .HeaderOpen => |info| { try out.print( "
See also:
$ zig build-exe {}.zig", .{code.name});
switch (code.mode) {
- builtin.Mode.Debug => {},
- builtin.Mode.ReleaseSafe => {
+ .Debug => {},
+ .ReleaseSafe => {
try build_args.append("--release-safe");
try out.print(" --release-safe", .{});
},
- builtin.Mode.ReleaseFast => {
+ .ReleaseFast => {
try build_args.append("--release-fast");
try out.print(" --release-fast", .{});
},
- builtin.Mode.ReleaseSmall => {
+ .ReleaseSmall => {
try build_args.append("--release-small");
try out.print(" --release-small", .{});
},
@@ -1142,13 +1142,14 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
try out.print("\n{}
\n", .{colored_stderr});
break :code_block;
}
- const exec_result = exec(allocator, &env_map, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile", .{});
+ const exec_result = exec(allocator, &env_map, build_args.toSliceConst()) catch
+ return parseError(tokenizer, code.source_token, "example failed to compile", .{});
if (code.target_str) |triple| {
if (mem.startsWith(u8, triple, "wasm32") or
mem.startsWith(u8, triple, "riscv64-linux") or
- mem.startsWith(u8, triple, "x86_64-linux") and
- (builtin.os != .linux or builtin.arch != .x86_64))
+ (mem.startsWith(u8, triple, "x86_64-linux") and
+ std.Target.current.os.tag != .linux or std.Target.current.cpu.arch != .x86_64))
{
// skip execution
try out.print("\n", .{});
@@ -1207,16 +1208,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
});
try out.print("$ zig test {}.zig", .{code.name});
switch (code.mode) {
- builtin.Mode.Debug => {},
- builtin.Mode.ReleaseSafe => {
+ .Debug => {},
+ .ReleaseSafe => {
try test_args.append("--release-safe");
try out.print(" --release-safe", .{});
},
- builtin.Mode.ReleaseFast => {
+ .ReleaseFast => {
try test_args.append("--release-fast");
try out.print(" --release-fast", .{});
},
- builtin.Mode.ReleaseSmall => {
+ .ReleaseSmall => {
try test_args.append("--release-small");
try out.print(" --release-small", .{});
},
@@ -1249,16 +1250,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
});
try out.print("$ zig test {}.zig", .{code.name});
switch (code.mode) {
- builtin.Mode.Debug => {},
- builtin.Mode.ReleaseSafe => {
+ .Debug => {},
+ .ReleaseSafe => {
try test_args.append("--release-safe");
try out.print(" --release-safe", .{});
},
- builtin.Mode.ReleaseFast => {
+ .ReleaseFast => {
try test_args.append("--release-fast");
try out.print(" --release-fast", .{});
},
- builtin.Mode.ReleaseSmall => {
+ .ReleaseSmall => {
try test_args.append("--release-small");
try out.print(" --release-small", .{});
},
@@ -1306,16 +1307,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
});
var mode_arg: []const u8 = "";
switch (code.mode) {
- builtin.Mode.Debug => {},
- builtin.Mode.ReleaseSafe => {
+ .Debug => {},
+ .ReleaseSafe => {
try test_args.append("--release-safe");
mode_arg = " --release-safe";
},
- builtin.Mode.ReleaseFast => {
+ .ReleaseFast => {
try test_args.append("--release-fast");
mode_arg = " --release-fast";
},
- builtin.Mode.ReleaseSmall => {
+ .ReleaseSmall => {
try test_args.append("--release-small");
mode_arg = " --release-small";
},
@@ -1386,20 +1387,20 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
}
switch (code.mode) {
- builtin.Mode.Debug => {},
- builtin.Mode.ReleaseSafe => {
+ .Debug => {},
+ .ReleaseSafe => {
try build_args.append("--release-safe");
if (!code.is_inline) {
try out.print(" --release-safe", .{});
}
},
- builtin.Mode.ReleaseFast => {
+ .ReleaseFast => {
try build_args.append("--release-fast");
if (!code.is_inline) {
try out.print(" --release-fast", .{});
}
},
- builtin.Mode.ReleaseSmall => {
+ .ReleaseSmall => {
try build_args.append("--release-small");
if (!code.is_inline) {
try out.print(" --release-small", .{});
@@ -1461,16 +1462,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
});
try out.print("$ zig build-lib {}.zig", .{code.name});
switch (code.mode) {
- builtin.Mode.Debug => {},
- builtin.Mode.ReleaseSafe => {
+ .Debug => {},
+ .ReleaseSafe => {
try test_args.append("--release-safe");
try out.print(" --release-safe", .{});
},
- builtin.Mode.ReleaseFast => {
+ .ReleaseFast => {
try test_args.append("--release-fast");
try out.print(" --release-fast", .{});
},
- builtin.Mode.ReleaseSmall => {
+ .ReleaseSmall => {
try test_args.append("--release-small");
try out.print(" --release-small", .{});
},
diff --git a/doc/langref.html.in b/doc/langref.html.in
index e963e39af7..31def074d7 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -965,7 +965,8 @@ const nan = std.math.nan(f128);
but you can switch to {#syntax#}Optimized{#endsyntax#} mode on a per-block basis:
{#code_begin|obj|foo#}
{#code_release_fast#}
-const builtin = @import("builtin");
+const std = @import("std");
+const builtin = std.builtin;
const big = @as(f64, 1 << 40);
export fn foo_strict(x: f64) f64 {
@@ -2063,15 +2064,15 @@ test "pointer child type" {
alignment of the underlying type, it can be omitted from the type:
{#code_begin|test#}
-const assert = @import("std").debug.assert;
-const builtin = @import("builtin");
+const std = @import("std");
+const assert = std.debug.assert;
test "variable alignment" {
var x: i32 = 1234;
const align_of_i32 = @alignOf(@TypeOf(x));
assert(@TypeOf(&x) == *i32);
assert(*i32 == *align(align_of_i32) i32);
- if (builtin.arch == builtin.Arch.x86_64) {
+ if (std.Target.current.cpu.arch == .x86_64) {
assert((*i32).alignment == 4);
}
}
@@ -2474,7 +2475,7 @@ test "default struct initialization fields" {
{#code_begin|test#}
const std = @import("std");
-const builtin = @import("builtin");
+const builtin = std.builtin;
const assert = std.debug.assert;
const Full = packed struct {
@@ -3204,8 +3205,8 @@ test "separate scopes" {
{#header_open|switch#}
{#code_begin|test|switch#}
-const assert = @import("std").debug.assert;
-const builtin = @import("builtin");
+const std = @import("std");
+const assert = std.debug.assert;
test "switch simple" {
const a: u64 = 10;
@@ -3249,16 +3250,16 @@ test "switch simple" {
}
// Switch expressions can be used outside a function:
-const os_msg = switch (builtin.os) {
- builtin.Os.linux => "we found a linux user",
+const os_msg = switch (std.Target.current.os.tag) {
+ .linux => "we found a linux user",
else => "not a linux user",
};
// Inside a function, switch statements implicitly are compile-time
// evaluated if the target expression is compile-time known.
test "switch inside function" {
- switch (builtin.os) {
- builtin.Os.fuchsia => {
+ switch (std.Target.current.os.tag) {
+ .fuchsia => {
// On an OS other than fuchsia, block is not even analyzed,
// so this compile error is not triggered.
// On fuchsia this compile error would be triggered.
@@ -7330,8 +7331,6 @@ test "main" {
the {#syntax#}export{#endsyntax#} keyword used on a function:
{#code_begin|obj#}
-const builtin = @import("builtin");
-
comptime {
@export(internalName, .{ .name = "foo", .linkage = .Strong });
}
@@ -9363,7 +9362,7 @@ const separator = if (builtin.os == builtin.Os.windows) '\\' else '/';
{#code_begin|test|detect_test#}
const std = @import("std");
-const builtin = @import("builtin");
+const builtin = std.builtin;
const assert = std.debug.assert;
test "builtin.is_test" {
@@ -9681,7 +9680,8 @@ WebAssembly.instantiate(typedArray, {
$ node test.js
The result is 3
{#header_open|WASI#}
- Zig's support for WebAssembly System Interface (WASI) is under active development. Example of using the standard library and reading command line arguments:
+ Zig's support for WebAssembly System Interface (WASI) is under active development.
+ Example of using the standard library and reading command line arguments:
{#code_begin|exe|wasi#}
{#target_wasi#}
const std = @import("std");
diff --git a/lib/std/build.zig b/lib/std/build.zig
index 29837d56d9..ecf3930551 100644
--- a/lib/std/build.zig
+++ b/lib/std/build.zig
@@ -1,5 +1,5 @@
const std = @import("std.zig");
-const builtin = @import("builtin");
+const builtin = std.builtin;
const io = std.io;
const fs = std.fs;
const mem = std.mem;
@@ -15,6 +15,7 @@ const BufSet = std.BufSet;
const BufMap = std.BufMap;
const fmt_lib = std.fmt;
const File = std.fs.File;
+const CrossTarget = std.zig.CrossTarget;
pub const FmtStep = @import("build/fmt.zig").FmtStep;
pub const TranslateCStep = @import("build/translate_c.zig").TranslateCStep;
@@ -521,24 +522,91 @@ pub const Builder = struct {
return mode;
}
- /// Exposes standard `zig build` options for choosing a target. Pass `null` to support all targets.
- pub fn standardTargetOptions(self: *Builder, supported_targets: ?[]const Target) Target {
- if (supported_targets) |target_list| {
- // TODO detect multiple args and emit an error message
- // there's probably a better way to collect the target
- for (target_list) |targ| {
- const targ_str = targ.zigTriple(self.allocator) catch unreachable;
- const targ_desc = targ.allocDescription(self.allocator) catch unreachable;
- const this_targ_opt = self.option(bool, targ_str, targ_desc) orelse false;
- if (this_targ_opt) {
- return targ;
+ pub const StandardTargetOptionsArgs = struct {
+ whitelist: ?[]const CrossTarget = null,
+
+ default_target: CrossTarget = CrossTarget{},
+ };
+
+ /// Exposes standard `zig build` options for choosing a target.
+ pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget {
+ const triple = self.option(
+ []const u8,
+ "target",
+ "The CPU architecture, OS, and ABI to build for.",
+ ) orelse return args.default_target;
+
+ // TODO add cpu and features as part of the target triple
+
+ var diags: CrossTarget.ParseOptions.Diagnostics = .{};
+ const selected_target = CrossTarget.parse(.{
+ .arch_os_abi = triple,
+ .diagnostics = &diags,
+ }) catch |err| switch (err) {
+ error.UnknownCpuModel => {
+ std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
+ diags.cpu_name.?,
+ @tagName(diags.arch.?),
+ });
+ for (diags.arch.?.allCpuModels()) |cpu| {
+ std.debug.warn(" {}\n", .{cpu.name});
+ }
+ process.exit(1);
+ },
+ error.UnknownCpuFeature => {
+ std.debug.warn(
+ \\Unknown CPU feature: '{}'
+ \\Available CPU features for architecture '{}':
+ \\
+ , .{
+ diags.unknown_feature_name,
+ @tagName(diags.arch.?),
+ });
+ for (diags.arch.?.allFeaturesList()) |feature| {
+ std.debug.warn(" {}: {}\n", .{ feature.name, feature.description });
+ }
+ process.exit(1);
+ },
+ error.UnknownOperatingSystem => {
+ std.debug.warn(
+ \\Unknown OS: '{}'
+ \\Available operating systems:
+ \\
+ , .{diags.os_name});
+ inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
+ std.debug.warn(" {}\n", .{field.name});
+ }
+ process.exit(1);
+ },
+ else => |e| {
+ std.debug.warn("Unable to parse target '{}': {}\n", .{ triple, @errorName(e) });
+ process.exit(1);
+ },
+ };
+
+ const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable;
+
+ if (args.whitelist) |list| whitelist_check: {
+ // Make sure it's a match of one of the list.
+ for (list) |t| {
+ const t_triple = t.zigTriple(self.allocator) catch unreachable;
+ if (mem.eql(u8, t_triple, selected_canonicalized_triple)) {
+ break :whitelist_check;
}
}
- return Target.Native;
- } else {
- const target_str = self.option([]const u8, "target", "the target to build for") orelse return Target.Native;
- return Target.parse(.{ .arch_os_abi = target_str }) catch unreachable; // TODO better error message for bad target
+ std.debug.warn("Chosen target '{}' does not match one of the supported targets:\n", .{
+ selected_canonicalized_triple,
+ });
+ for (list) |t| {
+ const t_triple = t.zigTriple(self.allocator) catch unreachable;
+ std.debug.warn(" {}\n", .{t_triple});
+ }
+ // TODO instead of process exit, return error and have a zig build flag implemented by
+ // the build runner that turns process exits into error return traces
+ process.exit(1);
}
+
+ return selected_target;
}
pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool {
@@ -796,7 +864,7 @@ pub const Builder = struct {
pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 {
// TODO report error for ambiguous situations
- const exe_extension = (Target{ .Native = {} }).exeFileExt();
+ const exe_extension = @as(CrossTarget, .{}).exeFileExt();
for (self.search_prefixes.toSliceConst()) |search_prefix| {
for (names) |name| {
if (fs.path.isAbsolute(name)) {
@@ -971,21 +1039,19 @@ pub const Builder = struct {
};
test "builder.findProgram compiles" {
- // TODO: uncomment and fix the leak
- // const builder = try Builder.create(std.testing.allocator, "zig", "zig-cache", "zig-cache");
- const builder = try Builder.create(std.heap.page_allocator, "zig", "zig-cache", "zig-cache");
+ var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
+ defer arena.deinit();
+
+ const builder = try Builder.create(&arena.allocator, "zig", "zig-cache", "zig-cache");
defer builder.destroy();
_ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
}
-/// Deprecated. Use `builtin.Version`.
+/// Deprecated. Use `std.builtin.Version`.
pub const Version = builtin.Version;
-/// Deprecated. Use `std.Target.Cross`.
-pub const CrossTarget = std.Target.Cross;
-
-/// Deprecated. Use `std.Target`.
-pub const Target = std.Target;
+/// Deprecated. Use `std.zig.CrossTarget`.
+pub const Target = std.zig.CrossTarget;
pub const Pkg = struct {
name: []const u8,
@@ -1038,7 +1104,7 @@ pub const LibExeObjStep = struct {
step: Step,
builder: *Builder,
name: []const u8,
- target: Target,
+ target: CrossTarget = CrossTarget{},
linker_script: ?[]const u8 = null,
version_script: ?[]const u8 = null,
out_filename: []const u8,
@@ -1076,7 +1142,7 @@ pub const LibExeObjStep = struct {
out_pdb_filename: []const u8,
packages: ArrayList(Pkg),
build_options_contents: std.Buffer,
- system_linker_hack: bool,
+ system_linker_hack: bool = false,
object_src: []const u8,
@@ -1091,7 +1157,6 @@ pub const LibExeObjStep = struct {
install_step: ?*InstallArtifactStep,
libc_file: ?[]const u8 = null,
- target_glibc: ?Version = null,
valgrind_support: ?bool = null,
@@ -1112,8 +1177,6 @@ pub const LibExeObjStep = struct {
/// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
glibc_multi_install_dir: ?[]const u8 = null,
- dynamic_linker: ?[]const u8 = null,
-
/// Position Independent Code
force_pic: ?bool = null,
@@ -1191,7 +1254,6 @@ pub const LibExeObjStep = struct {
.kind = kind,
.root_src = root_src,
.name = name,
- .target = Target.Native,
.frameworks = BufSet.init(builder.allocator),
.step = Step.init(name, builder.allocator, make),
.version = ver,
@@ -1210,7 +1272,6 @@ pub const LibExeObjStep = struct {
.object_src = undefined,
.build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable,
.c_std = Builder.CStd.C99,
- .system_linker_hack = false,
.override_lib_dir = null,
.main_pkg_path = null,
.exec_cmd_args = null,
@@ -1282,36 +1343,11 @@ pub const LibExeObjStep = struct {
}
}
- /// Deprecated. Use `setTheTarget`.
- pub fn setTarget(
- self: *LibExeObjStep,
- target_arch: builtin.Arch,
- target_os: builtin.Os,
- target_abi: builtin.Abi,
- ) void {
- return self.setTheTarget(Target{
- .Cross = CrossTarget{
- .arch = target_arch,
- .os = target_os,
- .abi = target_abi,
- .cpu_features = target_arch.getBaselineCpuFeatures(),
- },
- });
- }
-
- pub fn setTheTarget(self: *LibExeObjStep, target: Target) void {
+ pub fn setTarget(self: *LibExeObjStep, target: CrossTarget) void {
self.target = target;
self.computeOutFileNames();
}
- pub fn setTargetGLibC(self: *LibExeObjStep, major: u32, minor: u32, patch: u32) void {
- self.target_glibc = Version{
- .major = major,
- .minor = minor,
- .patch = patch,
- };
- }
-
pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void {
self.output_dir = self.builder.dupePath(dir);
}
@@ -1692,7 +1728,7 @@ pub const LibExeObjStep = struct {
.NotFound => return error.VcpkgNotFound,
.Found => |root| {
const allocator = self.builder.allocator;
- const triplet = try Target.vcpkgTriplet(allocator, self.target, linkage);
+ const triplet = try self.target.vcpkgTriplet(allocator, linkage);
defer self.builder.allocator.free(triplet);
const include_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "include" });
@@ -1862,10 +1898,10 @@ pub const LibExeObjStep = struct {
}
switch (self.build_mode) {
- builtin.Mode.Debug => {},
- builtin.Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,
- builtin.Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable,
- builtin.Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable,
+ .Debug => {},
+ .ReleaseSafe => zig_args.append("--release-safe") catch unreachable,
+ .ReleaseFast => zig_args.append("--release-fast") catch unreachable,
+ .ReleaseSmall => zig_args.append("--release-small") catch unreachable,
}
try zig_args.append("--cache-dir");
@@ -1905,47 +1941,46 @@ pub const LibExeObjStep = struct {
try zig_args.append(@tagName(self.code_model));
}
- switch (self.target) {
- .Native => {},
- .Cross => |cross| {
- try zig_args.append("-target");
- try zig_args.append(self.target.zigTriple(builder.allocator) catch unreachable);
+ if (!self.target.isNative()) {
+ try zig_args.append("-target");
+ try zig_args.append(try self.target.zigTriple(builder.allocator));
- const all_features = self.target.getArch().allFeaturesList();
- var populated_cpu_features = cross.cpu.model.features;
- populated_cpu_features.populateDependencies(all_features);
+ // TODO this logic can disappear if cpu model + features becomes part of the target triple
+ const cross = self.target.toTarget();
+ const all_features = cross.cpu.arch.allFeaturesList();
+ var populated_cpu_features = cross.cpu.model.features;
+ populated_cpu_features.populateDependencies(all_features);
- if (populated_cpu_features.eql(cross.cpu.features)) {
- // The CPU name alone is sufficient.
- // If it is the baseline CPU, no command line args are required.
- if (cross.cpu.model != Target.Cpu.baseline(self.target.getArch()).model) {
- try zig_args.append("-mcpu");
- try zig_args.append(cross.cpu.model.name);
- }
- } else {
- var mcpu_buffer = try std.Buffer.init(builder.allocator, "-mcpu=");
- try mcpu_buffer.append(cross.cpu.model.name);
-
- for (all_features) |feature, i_usize| {
- const i = @intCast(Target.Cpu.Feature.Set.Index, i_usize);
- const in_cpu_set = populated_cpu_features.isEnabled(i);
- const in_actual_set = cross.cpu.features.isEnabled(i);
- if (in_cpu_set and !in_actual_set) {
- try mcpu_buffer.appendByte('-');
- try mcpu_buffer.append(feature.name);
- } else if (!in_cpu_set and in_actual_set) {
- try mcpu_buffer.appendByte('+');
- try mcpu_buffer.append(feature.name);
- }
- }
- try zig_args.append(mcpu_buffer.toSliceConst());
+ if (populated_cpu_features.eql(cross.cpu.features)) {
+ // The CPU name alone is sufficient.
+ // If it is the baseline CPU, no command line args are required.
+ if (cross.cpu.model != std.Target.Cpu.baseline(cross.cpu.arch).model) {
+ try zig_args.append("-mcpu");
+ try zig_args.append(cross.cpu.model.name);
}
- },
- }
+ } else {
+ var mcpu_buffer = try std.Buffer.init(builder.allocator, "-mcpu=");
+ try mcpu_buffer.append(cross.cpu.model.name);
- if (self.target_glibc) |ver| {
- try zig_args.append("-target-glibc");
- try zig_args.append(builder.fmt("{}.{}.{}", .{ ver.major, ver.minor, ver.patch }));
+ for (all_features) |feature, i_usize| {
+ const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
+ const in_cpu_set = populated_cpu_features.isEnabled(i);
+ const in_actual_set = cross.cpu.features.isEnabled(i);
+ if (in_cpu_set and !in_actual_set) {
+ try mcpu_buffer.appendByte('-');
+ try mcpu_buffer.append(feature.name);
+ } else if (!in_cpu_set and in_actual_set) {
+ try mcpu_buffer.appendByte('+');
+ try mcpu_buffer.append(feature.name);
+ }
+ }
+ try zig_args.append(mcpu_buffer.toSliceConst());
+ }
+
+ if (self.target.dynamic_linker.get()) |dynamic_linker| {
+ try zig_args.append("--dynamic-linker");
+ try zig_args.append(dynamic_linker);
+ }
}
if (self.linker_script) |linker_script| {
@@ -1953,11 +1988,6 @@ pub const LibExeObjStep = struct {
zig_args.append(builder.pathFromRoot(linker_script)) catch unreachable;
}
- if (self.dynamic_linker) |dynamic_linker| {
- try zig_args.append("--dynamic-linker");
- try zig_args.append(dynamic_linker);
- }
-
if (self.version_script) |version_script| {
try zig_args.append("--version-script");
try zig_args.append(builder.pathFromRoot(version_script));
@@ -1975,7 +2005,7 @@ pub const LibExeObjStep = struct {
} else switch (self.target.getExternalExecutor()) {
.native, .unavailable => {},
.qemu => |bin_name| if (self.enable_qemu) qemu: {
- const need_cross_glibc = self.target.isGnu() and self.target.isLinux() and self.is_linking_libc;
+ const need_cross_glibc = self.target.isGnuLibC() and self.is_linking_libc;
const glibc_dir_arg = if (need_cross_glibc)
self.glibc_multi_install_dir orelse break :qemu
else
@@ -2420,10 +2450,7 @@ const VcpkgRootStatus = enum {
Found,
};
-pub const VcpkgLinkage = enum {
- Static,
- Dynamic,
-};
+pub const VcpkgLinkage = std.builtin.LinkMode;
pub const InstallDir = enum {
Prefix,
diff --git a/lib/std/build/run.zig b/lib/std/build/run.zig
index 75809bde03..eacf408ba9 100644
--- a/lib/std/build/run.zig
+++ b/lib/std/build/run.zig
@@ -82,7 +82,7 @@ pub const RunStep = struct {
var key: []const u8 = undefined;
var prev_path: ?[]const u8 = undefined;
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
key = "Path";
prev_path = env_map.get(key);
if (prev_path == null) {
diff --git a/lib/std/build/translate_c.zig b/lib/std/build/translate_c.zig
index 3d1bdad677..e9e61b190f 100644
--- a/lib/std/build/translate_c.zig
+++ b/lib/std/build/translate_c.zig
@@ -7,6 +7,7 @@ const LibExeObjStep = build.LibExeObjStep;
const CheckFileStep = build.CheckFileStep;
const fs = std.fs;
const mem = std.mem;
+const CrossTarget = std.zig.CrossTarget;
pub const TranslateCStep = struct {
step: Step,
@@ -14,7 +15,7 @@ pub const TranslateCStep = struct {
source: build.FileSource,
output_dir: ?[]const u8,
out_basename: []const u8,
- target: std.Target = .Native,
+ target: CrossTarget = CrossTarget{},
pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep {
const self = builder.allocator.create(TranslateCStep) catch unreachable;
@@ -39,7 +40,7 @@ pub const TranslateCStep = struct {
) catch unreachable;
}
- pub fn setTarget(self: *TranslateCStep, target: std.Target) void {
+ pub fn setTarget(self: *TranslateCStep, target: CrossTarget) void {
self.target = target;
}
@@ -63,12 +64,9 @@ pub const TranslateCStep = struct {
try argv_list.append("--cache");
try argv_list.append("on");
- switch (self.target) {
- .Native => {},
- .Cross => {
- try argv_list.append("-target");
- try argv_list.append(try self.target.zigTriple(self.builder.allocator));
- },
+ if (!self.target.isNative()) {
+ try argv_list.append("-target");
+ try argv_list.append(try self.target.zigTriple(self.builder.allocator));
}
try argv_list.append(self.source.getPath(self.builder));
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index f57998c567..08fc85871b 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -185,6 +185,7 @@ pub const TypeInfo = union(enum) {
child: type,
is_allowzero: bool,
+ /// This field is an optional type.
/// The type of the sentinel is the element type of the pointer, which is
/// the value of the `child` field in this struct. However there is no way
/// to refer to that type here, so we use `var`.
@@ -206,6 +207,7 @@ pub const TypeInfo = union(enum) {
len: comptime_int,
child: type,
+ /// This field is an optional type.
/// The type of the sentinel is the element type of the array, which is
/// the value of the `child` field in this struct. However there is no way
/// to refer to that type here, so we use `var`.
@@ -398,7 +400,60 @@ pub const LinkMode = enum {
pub const Version = struct {
major: u32,
minor: u32,
- patch: u32,
+ patch: u32 = 0,
+
+ pub const Range = struct {
+ min: Version,
+ max: Version,
+
+ pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool {
+ if (self.min.compare(ver) == .gt) return false;
+ if (self.max.compare(ver) == .lt) return false;
+ return true;
+ }
+ };
+
+ pub fn order(lhs: Version, rhs: Version) std.math.Order {
+ if (lhs.major < rhs.major) return .lt;
+ if (lhs.major > rhs.major) return .gt;
+ if (lhs.minor < rhs.minor) return .lt;
+ if (lhs.minor > rhs.minor) return .gt;
+ if (lhs.patch < rhs.patch) return .lt;
+ if (lhs.patch > rhs.patch) return .gt;
+ return .eq;
+ }
+
+ pub fn parse(text: []const u8) !Version {
+ var it = std.mem.separate(text, ".");
+ return Version{
+ .major = try std.fmt.parseInt(u32, it.next() orelse return error.InvalidVersion, 10),
+ .minor = try std.fmt.parseInt(u32, it.next() orelse "0", 10),
+ .patch = try std.fmt.parseInt(u32, it.next() orelse "0", 10),
+ };
+ }
+
+ pub fn format(
+ self: Version,
+ comptime fmt: []const u8,
+ options: std.fmt.FormatOptions,
+ context: var,
+ comptime Error: type,
+ comptime output: fn (@TypeOf(context), []const u8) Error!void,
+ ) Error!void {
+ if (fmt.len == 0) {
+ if (self.patch == 0) {
+ if (self.minor == 0) {
+ return std.fmt.format(context, Error, output, "{}", .{self.major});
+ } else {
+ return std.fmt.format(context, Error, output, "{}.{}", .{ self.major, self.minor });
+ }
+ } else {
+ return std.fmt.format(context, Error, output, "{}.{}.{}", .{ self.major, self.minor, self.patch });
+ }
+ } else {
+ @compileError("Unknown format string: '" ++ fmt ++ "'");
+ }
+ }
};
/// This data structure is used by the Zig language code generation and
@@ -476,7 +531,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
root.os.panic(msg, error_return_trace);
unreachable;
}
- switch (os) {
+ switch (os.tag) {
.freestanding => {
while (true) {
@breakpoint();
diff --git a/lib/std/c.zig b/lib/std/c.zig
index f6c0e07dbd..48a3039f51 100644
--- a/lib/std/c.zig
+++ b/lib/std/c.zig
@@ -1,5 +1,5 @@
-const builtin = @import("builtin");
const std = @import("std");
+const builtin = std.builtin;
const page_size = std.mem.page_size;
pub const tokenizer = @import("c/tokenizer.zig");
@@ -10,7 +10,7 @@ pub const ast = @import("c/ast.zig");
pub usingnamespace @import("os/bits.zig");
-pub usingnamespace switch (builtin.os) {
+pub usingnamespace switch (std.Target.current.os.tag) {
.linux => @import("c/linux.zig"),
.windows => @import("c/windows.zig"),
.macosx, .ios, .tvos, .watchos => @import("c/darwin.zig"),
@@ -46,17 +46,16 @@ pub fn versionCheck(glibc_version: builtin.Version) type {
return struct {
pub const ok = blk: {
if (!builtin.link_libc) break :blk false;
- switch (builtin.abi) {
- .musl, .musleabi, .musleabihf => break :blk true,
- .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => {
- const ver = builtin.glibc_version orelse break :blk false;
- if (ver.major < glibc_version.major) break :blk false;
- if (ver.major > glibc_version.major) break :blk true;
- if (ver.minor < glibc_version.minor) break :blk false;
- if (ver.minor > glibc_version.minor) break :blk true;
- break :blk ver.patch >= glibc_version.patch;
- },
- else => break :blk false,
+ if (std.Target.current.abi.isMusl()) break :blk true;
+ if (std.Target.current.isGnuLibC()) {
+ const ver = std.Target.current.os.version_range.linux.glibc;
+ const order = ver.order(glibc_version);
+ break :blk switch (order) {
+ .gt, .eq => true,
+ .lt => false,
+ };
+ } else {
+ break :blk false;
}
};
};
@@ -109,6 +108,7 @@ pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8
pub extern "c" fn dup(fd: fd_t) c_int;
pub extern "c" fn dup2(old_fd: fd_t, new_fd: fd_t) c_int;
pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize;
+pub extern "c" fn readlinkat(dirfd: fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize;
pub extern "c" fn realpath(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8;
pub extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int;
pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int;
@@ -125,6 +125,7 @@ pub extern "c" fn sysctlnametomib(name: [*:0]const u8, mibp: ?*c_int, sizep: ?*u
pub extern "c" fn tcgetattr(fd: fd_t, termios_p: *termios) c_int;
pub extern "c" fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) c_int;
pub extern "c" fn fcntl(fd: fd_t, cmd: c_int, ...) c_int;
+pub extern "c" fn uname(buf: *utsname) c_int;
pub extern "c" fn gethostname(name: [*]u8, len: usize) c_int;
pub extern "c" fn bind(socket: fd_t, address: ?*const sockaddr, address_len: socklen_t) c_int;
diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig
index 0f7abaaaa0..be32536d6f 100644
--- a/lib/std/c/linux.zig
+++ b/lib/std/c/linux.zig
@@ -94,7 +94,7 @@ pub const pthread_cond_t = extern struct {
size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T,
};
const __SIZEOF_PTHREAD_COND_T = 48;
-const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os == .fuchsia) 40 else switch (builtin.abi) {
+const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os.tag == .fuchsia) 40 else switch (builtin.abi) {
.musl, .musleabi, .musleabihf => if (@sizeOf(usize) == 8) 40 else 24,
.gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (builtin.arch) {
.aarch64 => 48,
diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig
index bb8ed2e8a0..d5e914b286 100644
--- a/lib/std/child_process.zig
+++ b/lib/std/child_process.zig
@@ -17,9 +17,9 @@ const TailQueue = std.TailQueue;
const maxInt = std.math.maxInt;
pub const ChildProcess = struct {
- pid: if (builtin.os == .windows) void else i32,
- handle: if (builtin.os == .windows) windows.HANDLE else void,
- thread_handle: if (builtin.os == .windows) windows.HANDLE else void,
+ pid: if (builtin.os.tag == .windows) void else i32,
+ handle: if (builtin.os.tag == .windows) windows.HANDLE else void,
+ thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void,
allocator: *mem.Allocator,
@@ -39,15 +39,15 @@ pub const ChildProcess = struct {
stderr_behavior: StdIo,
/// Set to change the user id when spawning the child process.
- uid: if (builtin.os == .windows) void else ?u32,
+ uid: if (builtin.os.tag == .windows) void else ?u32,
/// Set to change the group id when spawning the child process.
- gid: if (builtin.os == .windows) void else ?u32,
+ gid: if (builtin.os.tag == .windows) void else ?u32,
/// Set to change the current working directory when spawning the child process.
cwd: ?[]const u8,
- err_pipe: if (builtin.os == .windows) void else [2]os.fd_t,
+ err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t,
expand_arg0: Arg0Expand,
@@ -96,8 +96,8 @@ pub const ChildProcess = struct {
.term = null,
.env_map = null,
.cwd = null,
- .uid = if (builtin.os == .windows) {} else null,
- .gid = if (builtin.os == .windows) {} else null,
+ .uid = if (builtin.os.tag == .windows) {} else null,
+ .gid = if (builtin.os.tag == .windows) {} else null,
.stdin = null,
.stdout = null,
.stderr = null,
@@ -118,7 +118,7 @@ pub const ChildProcess = struct {
/// On success must call `kill` or `wait`.
pub fn spawn(self: *ChildProcess) SpawnError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return self.spawnWindows();
} else {
return self.spawnPosix();
@@ -132,7 +132,7 @@ pub const ChildProcess = struct {
/// Forcibly terminates child process and then cleans up all resources.
pub fn kill(self: *ChildProcess) !Term {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return self.killWindows(1);
} else {
return self.killPosix();
@@ -162,7 +162,7 @@ pub const ChildProcess = struct {
/// Blocks until child process terminates and then cleans up all resources.
pub fn wait(self: *ChildProcess) !Term {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return self.waitWindows();
} else {
return self.waitPosix();
@@ -307,7 +307,7 @@ pub const ChildProcess = struct {
fn cleanupAfterWait(self: *ChildProcess, status: u32) !Term {
defer destroyPipe(self.err_pipe);
- if (builtin.os == .linux) {
+ if (builtin.os.tag == .linux) {
var fd = [1]std.os.pollfd{std.os.pollfd{
.fd = self.err_pipe[0],
.events = std.os.POLLIN,
@@ -402,7 +402,7 @@ pub const ChildProcess = struct {
// This pipe is used to communicate errors between the time of fork
// and execve from the child process to the parent process.
const err_pipe = blk: {
- if (builtin.os == .linux) {
+ if (builtin.os.tag == .linux) {
const fd = try os.eventfd(0, 0);
// There's no distinction between the readable and the writeable
// end with eventfd
diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig
index 44326bfd97..0cc2c1d3ad 100644
--- a/lib/std/crypto/benchmark.zig
+++ b/lib/std/crypto/benchmark.zig
@@ -120,7 +120,7 @@ fn usage() void {
}
fn mode(comptime x: comptime_int) comptime_int {
- return if (builtin.mode == builtin.Mode.Debug) x / 64 else x;
+ return if (builtin.mode == .Debug) x / 64 else x;
}
// TODO(#1358): Replace with builtin formatted padding when available.
diff --git a/lib/std/cstr.zig b/lib/std/cstr.zig
index 765e0a45cf..9cb16b0ed3 100644
--- a/lib/std/cstr.zig
+++ b/lib/std/cstr.zig
@@ -4,8 +4,8 @@ const debug = std.debug;
const mem = std.mem;
const testing = std.testing;
-pub const line_sep = switch (builtin.os) {
- builtin.Os.windows => "\r\n",
+pub const line_sep = switch (builtin.os.tag) {
+ .windows => "\r\n",
else => "\n",
};
@@ -28,7 +28,7 @@ test "cstr fns" {
fn testCStrFnsImpl() void {
testing.expect(cmp("aoeu", "aoez") == -1);
- testing.expect(mem.len(u8, "123456789") == 9);
+ testing.expect(mem.len("123456789") == 9);
}
/// Returns a mutable, null-terminated slice with the same length as `slice`.
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
index efe4f1fa76..558b7e0513 100644
--- a/lib/std/debug.zig
+++ b/lib/std/debug.zig
@@ -1,4 +1,5 @@
const std = @import("std.zig");
+const builtin = std.builtin;
const math = std.math;
const mem = std.mem;
const io = std.io;
@@ -11,7 +12,6 @@ const macho = std.macho;
const coff = std.coff;
const pdb = std.pdb;
const ArrayList = std.ArrayList;
-const builtin = @import("builtin");
const root = @import("root");
const maxInt = std.math.maxInt;
const File = std.fs.File;
@@ -38,6 +38,18 @@ const Module = struct {
checksum_offset: ?usize,
};
+pub const LineInfo = struct {
+ line: u64,
+ column: u64,
+ file_name: []const u8,
+ allocator: ?*mem.Allocator,
+
+ fn deinit(self: LineInfo) void {
+ const allocator = self.allocator orelse return;
+ allocator.free(self.file_name);
+ }
+};
+
/// Tries to write to stderr, unbuffered, and ignores any error returned.
/// Does not append a newline.
var stderr_file: File = undefined;
@@ -89,7 +101,7 @@ pub fn detectTTYConfig() TTY.Config {
} else |_| {
if (stderr_file.supportsAnsiEscapeCodes()) {
return .escape_codes;
- } else if (builtin.os == .windows and stderr_file.isTty()) {
+ } else if (builtin.os.tag == .windows and stderr_file.isTty()) {
return .windows_api;
} else {
return .no_color;
@@ -143,7 +155,7 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {
/// chopping off the irrelevant frames and shifting so that the returned addresses pointer
/// equals the passed in addresses pointer.
pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace) void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const addrs = stack_trace.instruction_addresses;
const u32_addrs_len = @intCast(u32, addrs.len);
const first_addr = first_address orelse {
@@ -219,7 +231,7 @@ pub fn assert(ok: bool) void {
pub fn panic(comptime format: []const u8, args: var) noreturn {
@setCold(true);
// TODO: remove conditional once wasi / LLVM defines __builtin_return_address
- const first_trace_addr = if (builtin.os == .wasi) null else @returnAddress();
+ const first_trace_addr = if (builtin.os.tag == .wasi) null else @returnAddress();
panicExtra(null, first_trace_addr, format, args);
}
@@ -349,7 +361,7 @@ pub fn writeCurrentStackTrace(
tty_config: TTY.Config,
start_addr: ?usize,
) !void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return writeCurrentStackTraceWindows(out_stream, debug_info, tty_config, start_addr);
}
var it = StackIterator.init(start_addr, null);
@@ -378,175 +390,6 @@ pub fn writeCurrentStackTraceWindows(
}
}
-/// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented,
-/// make this `noasync fn` and remove the individual noasync calls.
-pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void {
- if (builtin.os == .windows) {
- return noasync printSourceAtAddressWindows(debug_info, out_stream, address, tty_config);
- }
- if (comptime std.Target.current.isDarwin()) {
- return noasync printSourceAtAddressMacOs(debug_info, out_stream, address, tty_config);
- }
- return noasync printSourceAtAddressPosix(debug_info, out_stream, address, tty_config);
-}
-
-/// TODO resources https://github.com/ziglang/zig/issues/4353
-fn printSourceAtAddressWindows(
- di: *DebugInfo,
- out_stream: var,
- relocated_address: usize,
- tty_config: TTY.Config,
-) !void {
- const allocator = getDebugInfoAllocator();
- const base_address = process.getBaseAddress();
- const relative_address = relocated_address - base_address;
-
- var coff_section: *coff.Section = undefined;
- const mod_index = for (di.sect_contribs) |sect_contrib| {
- if (sect_contrib.Section > di.coff.sections.len) continue;
- // Remember that SectionContribEntry.Section is 1-based.
- coff_section = &di.coff.sections.toSlice()[sect_contrib.Section - 1];
-
- const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;
- const vaddr_end = vaddr_start + sect_contrib.Size;
- if (relative_address >= vaddr_start and relative_address < vaddr_end) {
- break sect_contrib.ModuleIndex;
- }
- } else {
- // we have no information to add to the address
- return printLineInfo(out_stream, null, relocated_address, "???", "???", tty_config, printLineFromFileAnyOs);
- };
-
- const mod = &di.modules[mod_index];
- try populateModule(di, mod);
- const obj_basename = fs.path.basename(mod.obj_file_name);
-
- var symbol_i: usize = 0;
- const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) {
- const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]);
- if (prefix.RecordLen < 2)
- return error.InvalidDebugInfo;
- switch (prefix.RecordKind) {
- .S_LPROC32, .S_GPROC32 => {
- const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]);
- const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset;
- const vaddr_end = vaddr_start + proc_sym.CodeSize;
- if (relative_address >= vaddr_start and relative_address < vaddr_end) {
- break mem.toSliceConst(u8, @ptrCast([*:0]u8, proc_sym) + @sizeOf(pdb.ProcSym));
- }
- },
- else => {},
- }
- symbol_i += prefix.RecordLen + @sizeOf(u16);
- if (symbol_i > mod.symbols.len)
- return error.InvalidDebugInfo;
- } else "???";
-
- const subsect_info = mod.subsect_info;
-
- var sect_offset: usize = 0;
- var skip_len: usize = undefined;
- const opt_line_info = subsections: {
- const checksum_offset = mod.checksum_offset orelse break :subsections null;
- while (sect_offset != subsect_info.len) : (sect_offset += skip_len) {
- const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]);
- skip_len = subsect_hdr.Length;
- sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
-
- switch (subsect_hdr.Kind) {
- pdb.DebugSubsectionKind.Lines => {
- var line_index = sect_offset;
-
- const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]);
- if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo;
- line_index += @sizeOf(pdb.LineFragmentHeader);
- const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset;
- const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize;
-
- if (relative_address >= frag_vaddr_start and relative_address < frag_vaddr_end) {
- // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records)
- // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in,
- // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection.
- const subsection_end_index = sect_offset + subsect_hdr.Length;
-
- while (line_index < subsection_end_index) {
- const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]);
- line_index += @sizeOf(pdb.LineBlockFragmentHeader);
- const start_line_index = line_index;
-
- const has_column = line_hdr.Flags.LF_HaveColumns;
-
- // All line entries are stored inside their line block by ascending start address.
- // Heuristic: we want to find the last line entry that has a vaddr_start <= relative_address.
- // This is done with a simple linear search.
- var line_i: u32 = 0;
- while (line_i < block_hdr.NumLines) : (line_i += 1) {
- const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]);
- line_index += @sizeOf(pdb.LineNumberEntry);
-
- const vaddr_start = frag_vaddr_start + line_num_entry.Offset;
- if (relative_address < vaddr_start) {
- break;
- }
- }
-
- // line_i == 0 would mean that no matching LineNumberEntry was found.
- if (line_i > 0) {
- const subsect_index = checksum_offset + block_hdr.NameIndex;
- const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]);
- const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset;
- try di.pdb.string_table.seekTo(strtab_offset);
- const source_file_name = try di.pdb.string_table.readNullTermString(allocator);
-
- const line_entry_idx = line_i - 1;
-
- const column = if (has_column) blk: {
- const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines;
- const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx;
- const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]);
- break :blk col_num_entry.StartColumn;
- } else 0;
-
- const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry);
- const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]);
- const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags);
-
- break :subsections LineInfo{
- .allocator = allocator,
- .file_name = source_file_name,
- .line = flags.Start,
- .column = column,
- };
- }
- }
-
- // Checking that we are not reading garbage after the (possibly) multiple block fragments.
- if (line_index != subsection_end_index) {
- return error.InvalidDebugInfo;
- }
- }
- },
- else => {},
- }
-
- if (sect_offset > subsect_info.len)
- return error.InvalidDebugInfo;
- } else {
- break :subsections null;
- }
- };
-
- try printLineInfo(
- out_stream,
- opt_line_info,
- relocated_address,
- symbol_name,
- obj_basename,
- tty_config,
- printLineFromFileAnyOs,
- );
-}
-
pub const TTY = struct {
pub const Color = enum {
Red,
@@ -575,7 +418,7 @@ pub const TTY = struct {
.Dim => noasync out_stream.write(DIM) catch return,
.Reset => noasync out_stream.write(RESET) catch return,
},
- .windows_api => if (builtin.os == .windows) {
+ .windows_api => if (builtin.os.tag == .windows) {
const S = struct {
var attrs: windows.WORD = undefined;
var init_attrs = false;
@@ -618,7 +461,7 @@ pub const TTY = struct {
};
/// TODO resources https://github.com/ziglang/zig/issues/4353
-fn populateModule(di: *DebugInfo, mod: *Module) !void {
+fn populateModule(di: *ModuleDebugInfo, mod: *Module) !void {
if (mod.populated)
return;
const allocator = getDebugInfoAllocator();
@@ -650,7 +493,7 @@ fn populateModule(di: *DebugInfo, mod: *Module) !void {
sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
switch (subsect_hdr.Kind) {
- pdb.DebugSubsectionKind.FileChecksums => {
+ .FileChecksums => {
mod.checksum_offset = sect_offset;
break;
},
@@ -682,41 +525,37 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach
return null;
}
-fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void {
- const base_addr = process.getBaseAddress();
- const adjusted_addr = 0x100000000 + (address - base_addr);
-
- const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse {
- return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs);
- };
-
- const symbol_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + symbol.nlist.n_strx));
- const compile_unit_name = if (symbol.ofile) |ofile| blk: {
- const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx));
- break :blk fs.path.basename(ofile_path);
- } else "???";
-
- const line_info = getLineNumberInfoMacOs(di, symbol.*, adjusted_addr) catch |err| switch (err) {
- error.MissingDebugInfo, error.InvalidDebugInfo => null,
+/// TODO resources https://github.com/ziglang/zig/issues/4353
+pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void {
+ const module = debug_info.getModuleForAddress(address) catch |err| switch (err) {
+ error.MissingDebugInfo, error.InvalidDebugInfo => {
+ return printLineInfo(
+ out_stream,
+ null,
+ address,
+ "???",
+ "???",
+ tty_config,
+ printLineFromFileAnyOs,
+ );
+ },
else => return err,
};
- defer if (line_info) |li| li.deinit();
- try printLineInfo(
+ const symbol_info = try module.getSymbolAtAddress(address);
+ defer symbol_info.deinit();
+
+ return printLineInfo(
out_stream,
- line_info,
+ symbol_info.line_info,
address,
- symbol_name,
- compile_unit_name,
+ symbol_info.symbol_name,
+ symbol_info.compile_unit_name,
tty_config,
printLineFromFileAnyOs,
);
}
-pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void {
- return debug_info.printSourceAtAddress(out_stream, address, tty_config, printLineFromFileAnyOs);
-}
-
fn printLineInfo(
out_stream: var,
line_info: ?LineInfo,
@@ -772,29 +611,32 @@ pub const OpenSelfDebugInfoError = error{
/// TODO resources https://github.com/ziglang/zig/issues/4353
/// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented,
/// make this `noasync fn` and remove the individual noasync calls.
-pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo {
+pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
if (builtin.strip_debug_info)
return error.MissingDebugInfo;
if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {
return noasync root.os.debug.openSelfDebugInfo(allocator);
}
- if (builtin.os == .windows) {
- return noasync openSelfDebugInfoWindows(allocator);
+ switch (builtin.os.tag) {
+ .linux,
+ .freebsd,
+ .macosx,
+ .windows,
+ => return DebugInfo.init(allocator),
+ else => @compileError("openSelfDebugInfo unsupported for this platform"),
}
- if (comptime std.Target.current.isDarwin()) {
- return noasync openSelfDebugInfoMacOs(allocator);
- }
- return noasync openSelfDebugInfoPosix(allocator);
}
-fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
- const self_file = try fs.openSelfExe();
- defer self_file.close();
+/// TODO resources https://github.com/ziglang/zig/issues/4353
+fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo {
+ const coff_file = try std.fs.openFileAbsoluteW(coff_file_path.ptr, .{});
+ errdefer coff_file.close();
const coff_obj = try allocator.create(coff.Coff);
- coff_obj.* = coff.Coff.init(allocator, self_file);
+ coff_obj.* = coff.Coff.init(allocator, coff_file);
- var di = DebugInfo{
+ var di = ModuleDebugInfo{
+ .base_address = undefined,
.coff = coff_obj,
.pdb = undefined,
.sect_contribs = undefined,
@@ -958,109 +800,85 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {
return list.toOwnedSlice();
}
-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.sh_offset,
- .size = elf_header.sh_size,
- };
-}
-
-/// Initialize DWARF info. The caller has the responsibility to initialize most
-/// the DwarfInfo fields before calling. These fields can be left undefined:
-/// * abbrev_table_list
-/// * compile_unit_list
-pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void {
- di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator);
- di.compile_unit_list = ArrayList(CompileUnit).init(allocator);
- di.func_list = ArrayList(Func).init(allocator);
- try di.scanAllFunctions();
- try di.scanAllCompileUnits();
+fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 {
+ const start = try math.cast(usize, offset);
+ const end = start + try math.cast(usize, size);
+ return ptr[start..end];
}
/// TODO resources https://github.com/ziglang/zig/issues/4353
-pub fn openElfDebugInfo(
- allocator: *mem.Allocator,
- data: []u8,
-) !DwarfInfo {
- var seekable_stream = io.SliceSeekableInStream.init(data);
- var efile = try elf.Elf.openStream(
+pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo {
+ const mapped_mem = try mapWholeFile(elf_file_path);
+
+ var seekable_stream = io.SliceSeekableInStream.init(mapped_mem);
+ var efile = try noasync elf.Elf.openStream(
allocator,
- @ptrCast(*DwarfSeekableStream, &seekable_stream.seekable_stream),
- @ptrCast(*DwarfInStream, &seekable_stream.stream),
+ @ptrCast(*DW.DwarfSeekableStream, &seekable_stream.seekable_stream),
+ @ptrCast(*DW.DwarfInStream, &seekable_stream.stream),
);
- defer efile.close();
+ defer noasync efile.close();
- const debug_info = (try efile.findSection(".debug_info")) orelse
+ const debug_info = (try noasync efile.findSection(".debug_info")) orelse
return error.MissingDebugInfo;
- const debug_abbrev = (try efile.findSection(".debug_abbrev")) orelse
+ const debug_abbrev = (try noasync efile.findSection(".debug_abbrev")) orelse
return error.MissingDebugInfo;
- const debug_str = (try efile.findSection(".debug_str")) orelse
+ const debug_str = (try noasync efile.findSection(".debug_str")) orelse
return error.MissingDebugInfo;
- const debug_line = (try efile.findSection(".debug_line")) orelse
+ const debug_line = (try noasync efile.findSection(".debug_line")) orelse
return error.MissingDebugInfo;
- const opt_debug_ranges = try efile.findSection(".debug_ranges");
+ const opt_debug_ranges = try noasync efile.findSection(".debug_ranges");
- var di = DwarfInfo{
+ var di = DW.DwarfInfo{
.endian = efile.endian,
- .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_info = try chopSlice(mapped_mem, debug_info.sh_offset, debug_info.sh_size),
+ .debug_abbrev = try chopSlice(mapped_mem, debug_abbrev.sh_offset, debug_abbrev.sh_size),
+ .debug_str = try chopSlice(mapped_mem, debug_str.sh_offset, debug_str.sh_size),
+ .debug_line = try chopSlice(mapped_mem, debug_line.sh_offset, debug_line.sh_size),
.debug_ranges = if (opt_debug_ranges) |debug_ranges|
- data[@intCast(usize, debug_ranges.sh_offset)..@intCast(usize, debug_ranges.sh_offset + debug_ranges.sh_size)]
+ try chopSlice(mapped_mem, debug_ranges.sh_offset, debug_ranges.sh_size)
else
null,
};
- try openDwarfDebugInfo(&di, allocator);
- return di;
+ try noasync DW.openDwarfDebugInfo(&di, allocator);
+
+ return ModuleDebugInfo{
+ .base_address = undefined,
+ .dwarf = di,
+ .mapped_memory = mapped_mem,
+ };
}
/// TODO resources https://github.com/ziglang/zig/issues/4353
-fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DwarfInfo {
- var exe_file = try fs.openSelfExe();
- errdefer exe_file.close();
+fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !ModuleDebugInfo {
+ const mapped_mem = try mapWholeFile(macho_file_path);
- const exe_len = math.cast(usize, try exe_file.getEndPos()) catch
- return error.DebugInfoTooLarge;
- const exe_mmap = try os.mmap(
- null,
- exe_len,
- os.PROT_READ,
- os.MAP_SHARED,
- exe_file.handle,
- 0,
+ const hdr = @ptrCast(
+ *const macho.mach_header_64,
+ @alignCast(@alignOf(macho.mach_header_64), mapped_mem.ptr),
);
- errdefer os.munmap(exe_mmap);
+ if (hdr.magic != macho.MH_MAGIC_64)
+ return error.InvalidDebugInfo;
- return openElfDebugInfo(allocator, exe_mmap);
-}
-
-/// TODO resources https://github.com/ziglang/zig/issues/4353
-fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
- const hdr = &std.c._mh_execute_header;
- assert(hdr.magic == std.macho.MH_MAGIC_64);
-
- const hdr_base = @ptrCast([*]u8, hdr);
+ const hdr_base = @ptrCast([*]const u8, hdr);
var ptr = hdr_base + @sizeOf(macho.mach_header_64);
var ncmd: u32 = hdr.ncmds;
const symtab = while (ncmd != 0) : (ncmd -= 1) {
- const lc = @ptrCast(*std.macho.load_command, ptr);
+ const lc = @ptrCast(*const std.macho.load_command, ptr);
switch (lc.cmd) {
- std.macho.LC_SYMTAB => break @ptrCast(*std.macho.symtab_command, ptr),
+ std.macho.LC_SYMTAB => break @ptrCast(*const std.macho.symtab_command, ptr),
else => {},
}
ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);
} else {
return error.MissingDebugInfo;
};
- const syms = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms];
- const strings = @ptrCast([*]u8, hdr_base + symtab.stroff)[0..symtab.strsize];
+ const syms = @ptrCast([*]const macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms];
+ const strings = @ptrCast([*]const u8, hdr_base + symtab.stroff)[0..symtab.strsize :0];
const symbols_buf = try allocator.alloc(MachoSymbol, syms.len);
- var ofile: ?*macho.nlist_64 = null;
+ var ofile: ?*const macho.nlist_64 = null;
var reloc: u64 = 0;
var symbol_index: usize = 0;
var last_len: u64 = 0;
@@ -1108,8 +926,10 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
// This sort is so that we can binary search later.
std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan);
- return DebugInfo{
- .ofiles = DebugInfo.OFileTable.init(allocator),
+ return ModuleDebugInfo{
+ .base_address = undefined,
+ .mapped_memory = mapped_mem,
+ .ofiles = ModuleDebugInfo.OFileTable.init(allocator),
.symbols = symbols,
.strings = strings,
};
@@ -1148,8 +968,8 @@ fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {
}
const MachoSymbol = struct {
- nlist: *macho.nlist_64,
- ofile: ?*macho.nlist_64,
+ nlist: *const macho.nlist_64,
+ ofile: ?*const macho.nlist_64,
reloc: u64,
/// Returns the address from the macho file
@@ -1162,1057 +982,614 @@ const MachoSymbol = struct {
}
};
-pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);
-pub const DwarfInStream = io.InStream(anyerror);
+fn mapWholeFile(path: []const u8) ![]const u8 {
+ const file = try noasync fs.openFileAbsolute(path, .{ .always_blocking = true });
+ defer noasync file.close();
-pub const DwarfInfo = struct {
- endian: builtin.Endian,
- // No memory is owned by the DwarfInfo
- debug_info: []u8,
- debug_abbrev: []u8,
- debug_str: []u8,
- debug_line: []u8,
- debug_ranges: ?[]u8,
- // Filled later by the initializer
- abbrev_table_list: ArrayList(AbbrevTableHeader) = undefined,
- compile_unit_list: ArrayList(CompileUnit) = undefined,
- func_list: ArrayList(Func) = undefined,
+ const file_len = try math.cast(usize, try file.getEndPos());
+ const mapped_mem = try os.mmap(
+ null,
+ file_len,
+ os.PROT_READ,
+ os.MAP_SHARED,
+ file.handle,
+ 0,
+ );
+ errdefer os.munmap(mapped_mem);
- pub fn allocator(self: DwarfInfo) *mem.Allocator {
- return self.abbrev_table_list.allocator;
- }
+ return mapped_mem;
+}
- /// This function works in freestanding mode.
- /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void
- pub fn printSourceAtAddress(
- self: *DwarfInfo,
- out_stream: var,
- address: usize,
- tty_config: TTY.Config,
- comptime printLineFromFile: var,
- ) !void {
- const compile_unit = self.findCompileUnit(address) catch {
- return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFile);
+pub const DebugInfo = struct {
+ allocator: *mem.Allocator,
+ address_map: std.AutoHashMap(usize, *ModuleDebugInfo),
+
+ pub fn init(allocator: *mem.Allocator) DebugInfo {
+ return DebugInfo{
+ .allocator = allocator,
+ .address_map = std.AutoHashMap(usize, *ModuleDebugInfo).init(allocator),
};
-
- const compile_unit_name = try compile_unit.die.getAttrString(self, DW.AT_name);
- const symbol_name = self.getSymbolName(address) orelse "???";
- const line_info = self.getLineNumberInfo(compile_unit.*, address) catch |err| switch (err) {
- error.MissingDebugInfo, error.InvalidDebugInfo => null,
- else => return err,
- };
- defer if (line_info) |li| li.deinit();
-
- try printLineInfo(
- out_stream,
- line_info,
- address,
- symbol_name,
- compile_unit_name,
- tty_config,
- printLineFromFile,
- );
}
- fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 {
- for (di.func_list.toSliceConst()) |*func| {
- if (func.pc_range) |range| {
- if (address >= range.start and address < range.end) {
- return func.name;
- }
- }
- }
-
- return null;
+ pub fn deinit(self: *DebugInfo) void {
+ // TODO: resources https://github.com/ziglang/zig/issues/4353
+ self.address_map.deinit();
}
- fn scanAllFunctions(di: *DwarfInfo) !void {
- var s = io.SliceSeekableInStream.init(di.debug_info);
- var this_unit_offset: u64 = 0;
-
- while (true) {
- s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
- error.EndOfStream => return,
- else => return err,
- };
-
- var is_64: bool = undefined;
- const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
- if (unit_length == 0) return;
- const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
-
- const version = try s.stream.readInt(u16, di.endian);
- if (version < 2 or version > 5) return error.InvalidDebugInfo;
-
- const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
-
- const address_size = try s.stream.readByte();
- if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
-
- const compile_unit_pos = try s.seekable_stream.getPos();
- const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
-
- try s.seekable_stream.seekTo(compile_unit_pos);
-
- const next_unit_pos = this_unit_offset + next_offset;
-
- while ((try s.seekable_stream.getPos()) < next_unit_pos) {
- const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue;
- const after_die_offset = try s.seekable_stream.getPos();
-
- switch (die_obj.tag_id) {
- DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => {
- const fn_name = x: {
- var depth: i32 = 3;
- var this_die_obj = die_obj;
- // Prenvent endless loops
- while (depth > 0) : (depth -= 1) {
- if (this_die_obj.getAttr(DW.AT_name)) |_| {
- const name = try this_die_obj.getAttrString(di, DW.AT_name);
- break :x name;
- } else if (this_die_obj.getAttr(DW.AT_abstract_origin)) |ref| {
- // Follow the DIE it points to and repeat
- const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin);
- if (ref_offset > next_offset) return error.InvalidDebugInfo;
- try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
- this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
- } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| {
- // Follow the DIE it points to and repeat
- const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification);
- if (ref_offset > next_offset) return error.InvalidDebugInfo;
- try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
- this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
- } else {
- break :x null;
- }
- }
-
- break :x null;
- };
-
- const pc_range = x: {
- if (die_obj.getAttrAddr(DW.AT_low_pc)) |low_pc| {
- if (die_obj.getAttr(DW.AT_high_pc)) |high_pc_value| {
- const pc_end = switch (high_pc_value.*) {
- FormValue.Address => |value| value,
- FormValue.Const => |value| b: {
- const offset = try value.asUnsignedLe();
- break :b (low_pc + offset);
- },
- else => return error.InvalidDebugInfo,
- };
- break :x PcRange{
- .start = low_pc,
- .end = pc_end,
- };
- } else {
- break :x null;
- }
- } else |err| {
- if (err != error.MissingDebugInfo) return err;
- break :x null;
- }
- };
-
- try di.func_list.append(Func{
- .name = fn_name,
- .pc_range = pc_range,
- });
- },
- else => {},
- }
-
- try s.seekable_stream.seekTo(after_die_offset);
- }
-
- this_unit_offset += next_offset;
- }
+ pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
+ if (comptime std.Target.current.isDarwin())
+ return self.lookupModuleDyld(address)
+ else if (builtin.os.tag == .windows)
+ return self.lookupModuleWin32(address)
+ else
+ return self.lookupModuleDl(address);
}
- fn scanAllCompileUnits(di: *DwarfInfo) !void {
- var s = io.SliceSeekableInStream.init(di.debug_info);
- var this_unit_offset: u64 = 0;
+ fn lookupModuleDyld(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
+ const image_count = std.c._dyld_image_count();
- while (true) {
- s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
- error.EndOfStream => return,
- else => return err,
- };
+ var i: u32 = 0;
+ while (i < image_count) : (i += 1) {
+ const base_address = std.c._dyld_get_image_vmaddr_slide(i);
- var is_64: bool = undefined;
- const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
- if (unit_length == 0) return;
- const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
+ if (address < base_address) continue;
- const version = try s.stream.readInt(u16, di.endian);
- if (version < 2 or version > 5) return error.InvalidDebugInfo;
+ const header = std.c._dyld_get_image_header(i) orelse continue;
+ // The array of load commands is right after the header
+ var cmd_ptr = @intToPtr([*]u8, @ptrToInt(header) + @sizeOf(macho.mach_header_64));
- const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
+ var cmds = header.ncmds;
+ while (cmds != 0) : (cmds -= 1) {
+ const lc = @ptrCast(
+ *macho.load_command,
+ @alignCast(@alignOf(macho.load_command), cmd_ptr),
+ );
+ cmd_ptr += lc.cmdsize;
+ if (lc.cmd != macho.LC_SEGMENT_64) continue;
- const address_size = try s.stream.readByte();
- if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
+ const segment_cmd = @ptrCast(
+ *const std.macho.segment_command_64,
+ @alignCast(@alignOf(std.macho.segment_command_64), lc),
+ );
- const compile_unit_pos = try s.seekable_stream.getPos();
- const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
+ const rebased_address = address - base_address;
+ const seg_start = segment_cmd.vmaddr;
+ const seg_end = seg_start + segment_cmd.vmsize;
- try s.seekable_stream.seekTo(compile_unit_pos);
-
- const compile_unit_die = try di.allocator().create(Die);
- compile_unit_die.* = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
-
- if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo;
-
- const pc_range = x: {
- if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| {
- if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| {
- const pc_end = switch (high_pc_value.*) {
- FormValue.Address => |value| value,
- FormValue.Const => |value| b: {
- const offset = try value.asUnsignedLe();
- break :b (low_pc + offset);
- },
- else => return error.InvalidDebugInfo,
- };
- break :x PcRange{
- .start = low_pc,
- .end = pc_end,
- };
- } else {
- break :x null;
+ if (rebased_address >= seg_start and rebased_address < seg_end) {
+ if (self.address_map.getValue(base_address)) |obj_di| {
+ return obj_di;
}
- } else |err| {
- if (err != error.MissingDebugInfo) return err;
- break :x null;
- }
- };
- try di.compile_unit_list.append(CompileUnit{
- .version = version,
- .is_64 = is_64,
- .pc_range = pc_range,
- .die = compile_unit_die,
- });
+ const obj_di = try self.allocator.create(ModuleDebugInfo);
+ errdefer self.allocator.destroy(obj_di);
- this_unit_offset += next_offset;
- }
- }
-
- fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {
- for (di.compile_unit_list.toSlice()) |*compile_unit| {
- if (compile_unit.pc_range) |range| {
- if (target_address >= range.start and target_address < range.end) return compile_unit;
- }
- if (di.debug_ranges) |debug_ranges| {
- if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| {
- var s = io.SliceSeekableInStream.init(debug_ranges);
-
- // All the addresses in the list are relative to the value
- // specified by DW_AT_low_pc or to some other value encoded
- // in the list itself.
- // If no starting value is specified use zero.
- var base_address = compile_unit.die.getAttrAddr(DW.AT_low_pc) catch |err| switch (err) {
- error.MissingDebugInfo => 0,
+ const macho_path = mem.toSliceConst(u8, std.c._dyld_get_image_name(i));
+ obj_di.* = openMachODebugInfo(self.allocator, macho_path) catch |err| switch (err) {
+ error.FileNotFound => return error.MissingDebugInfo,
else => return err,
};
+ obj_di.base_address = base_address;
- try s.seekable_stream.seekTo(ranges_offset);
+ try self.address_map.putNoClobber(base_address, obj_di);
- while (true) {
- const begin_addr = try s.stream.readIntLittle(usize);
- const end_addr = try s.stream.readIntLittle(usize);
- if (begin_addr == 0 and end_addr == 0) {
- break;
- }
- // This entry selects a new value for the base address
- if (begin_addr == maxInt(usize)) {
- base_address = end_addr;
- continue;
- }
- if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) {
- return compile_unit;
- }
- }
- } else |err| {
- if (err != error.MissingDebugInfo) return err;
- continue;
+ return obj_di;
}
}
}
+
return error.MissingDebugInfo;
}
- /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
- /// seeks in the stream and parses it.
- fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable {
- for (di.abbrev_table_list.toSlice()) |*header| {
- if (header.offset == abbrev_offset) {
- return &header.table;
- }
- }
- try di.abbrev_table_list.append(AbbrevTableHeader{
- .offset = abbrev_offset,
- .table = try di.parseAbbrevTable(abbrev_offset),
- });
- return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table;
- }
+ fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
+ const process_handle = windows.kernel32.GetCurrentProcess();
- fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable {
- var s = io.SliceSeekableInStream.init(di.debug_abbrev);
-
- try s.seekable_stream.seekTo(offset);
- var result = AbbrevTable.init(di.allocator());
- errdefer result.deinit();
- while (true) {
- const abbrev_code = try leb.readULEB128(u64, &s.stream);
- if (abbrev_code == 0) return result;
- try result.append(AbbrevTableEntry{
- .abbrev_code = abbrev_code,
- .tag_id = try leb.readULEB128(u64, &s.stream),
- .has_children = (try s.stream.readByte()) == DW.CHILDREN_yes,
- .attrs = ArrayList(AbbrevAttr).init(di.allocator()),
- });
- const attrs = &result.items[result.len - 1].attrs;
-
- while (true) {
- const attr_id = try leb.readULEB128(u64, &s.stream);
- const form_id = try leb.readULEB128(u64, &s.stream);
- if (attr_id == 0 and form_id == 0) break;
- try attrs.append(AbbrevAttr{
- .attr_id = attr_id,
- .form_id = form_id,
- });
- }
- }
- }
-
- fn parseDie(di: *DwarfInfo, in_stream: var, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {
- const abbrev_code = try leb.readULEB128(u64, in_stream);
- if (abbrev_code == 0) return null;
- const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
-
- var result = Die{
- .tag_id = table_entry.tag_id,
- .has_children = table_entry.has_children,
- .attrs = ArrayList(Die.Attr).init(di.allocator()),
- };
- try result.attrs.resize(table_entry.attrs.len);
- for (table_entry.attrs.toSliceConst()) |attr, i| {
- result.attrs.items[i] = Die.Attr{
- .id = attr.attr_id,
- .value = try parseFormValue(di.allocator(), in_stream, attr.form_id, is_64),
- };
- }
- return result;
- }
-
- fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo {
- var s = io.SliceSeekableInStream.init(di.debug_line);
-
- const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);
- const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list);
-
- try s.seekable_stream.seekTo(line_info_offset);
-
- var is_64: bool = undefined;
- const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
- if (unit_length == 0) {
+ // Find how many modules are actually loaded
+ var dummy: windows.HMODULE = undefined;
+ var bytes_needed: windows.DWORD = undefined;
+ if (windows.kernel32.K32EnumProcessModules(
+ process_handle,
+ @ptrCast([*]windows.HMODULE, &dummy),
+ 0,
+ &bytes_needed,
+ ) == 0)
return error.MissingDebugInfo;
- }
- const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
- const version = try s.stream.readInt(u16, di.endian);
- // TODO support 3 and 5
- if (version != 2 and version != 4) return error.InvalidDebugInfo;
+ const needed_modules = bytes_needed / @sizeOf(windows.HMODULE);
- const prologue_length = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
- const prog_start_offset = (try s.seekable_stream.getPos()) + prologue_length;
+ // Fetch the complete module list
+ var modules = try self.allocator.alloc(windows.HMODULE, needed_modules);
+ defer self.allocator.free(modules);
+ if (windows.kernel32.K32EnumProcessModules(
+ process_handle,
+ modules.ptr,
+ try math.cast(windows.DWORD, modules.len * @sizeOf(windows.HMODULE)),
+ &bytes_needed,
+ ) == 0)
+ return error.MissingDebugInfo;
- const minimum_instruction_length = try s.stream.readByte();
- if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
+ // There's an unavoidable TOCTOU problem here, the module list may have
+ // changed between the two EnumProcessModules call.
+ // Pick the smallest amount of elements to avoid processing garbage.
+ const needed_modules_after = bytes_needed / @sizeOf(windows.HMODULE);
+ const loaded_modules = math.min(needed_modules, needed_modules_after);
- if (version >= 4) {
- // maximum_operations_per_instruction
- _ = try s.stream.readByte();
- }
+ for (modules[0..loaded_modules]) |module| {
+ var info: windows.MODULEINFO = undefined;
+ if (windows.kernel32.K32GetModuleInformation(
+ process_handle,
+ module,
+ &info,
+ @sizeOf(@TypeOf(info)),
+ ) == 0)
+ return error.MissingDebugInfo;
- const default_is_stmt = (try s.stream.readByte()) != 0;
- const line_base = try s.stream.readByteSigned();
+ const seg_start = @ptrToInt(info.lpBaseOfDll);
+ const seg_end = seg_start + info.SizeOfImage;
- const line_range = try s.stream.readByte();
- if (line_range == 0) return error.InvalidDebugInfo;
-
- const opcode_base = try s.stream.readByte();
-
- const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
-
- {
- var i: usize = 0;
- while (i < opcode_base - 1) : (i += 1) {
- standard_opcode_lengths[i] = try s.stream.readByte();
- }
- }
-
- var include_directories = ArrayList([]u8).init(di.allocator());
- try include_directories.append(compile_unit_cwd);
- while (true) {
- const dir = try readStringRaw(di.allocator(), &s.stream);
- if (dir.len == 0) break;
- try include_directories.append(dir);
- }
-
- var file_entries = ArrayList(FileEntry).init(di.allocator());
- var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
-
- while (true) {
- const file_name = try readStringRaw(di.allocator(), &s.stream);
- if (file_name.len == 0) break;
- const dir_index = try leb.readULEB128(usize, &s.stream);
- const mtime = try leb.readULEB128(usize, &s.stream);
- const len_bytes = try leb.readULEB128(usize, &s.stream);
- try file_entries.append(FileEntry{
- .file_name = file_name,
- .dir_index = dir_index,
- .mtime = mtime,
- .len_bytes = len_bytes,
- });
- }
-
- try s.seekable_stream.seekTo(prog_start_offset);
-
- const next_unit_pos = line_info_offset + next_offset;
-
- while ((try s.seekable_stream.getPos()) < next_unit_pos) {
- const opcode = try s.stream.readByte();
-
- if (opcode == DW.LNS_extended_op) {
- const op_size = try leb.readULEB128(u64, &s.stream);
- if (op_size < 1) return error.InvalidDebugInfo;
- var sub_op = try s.stream.readByte();
- switch (sub_op) {
- DW.LNE_end_sequence => {
- prog.end_sequence = true;
- if (try prog.checkLineMatch()) |info| return info;
- prog.reset();
- },
- DW.LNE_set_address => {
- const addr = try s.stream.readInt(usize, di.endian);
- prog.address = addr;
- },
- DW.LNE_define_file => {
- const file_name = try readStringRaw(di.allocator(), &s.stream);
- const dir_index = try leb.readULEB128(usize, &s.stream);
- const mtime = try leb.readULEB128(usize, &s.stream);
- const len_bytes = try leb.readULEB128(usize, &s.stream);
- try file_entries.append(FileEntry{
- .file_name = file_name,
- .dir_index = dir_index,
- .mtime = mtime,
- .len_bytes = len_bytes,
- });
- },
- else => {
- const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
- try s.seekable_stream.seekBy(fwd_amt);
- },
- }
- } else if (opcode >= opcode_base) {
- // special opcodes
- const adjusted_opcode = opcode - opcode_base;
- const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
- const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range);
- prog.line += inc_line;
- prog.address += inc_addr;
- if (try prog.checkLineMatch()) |info| return info;
- prog.basic_block = false;
- } else {
- switch (opcode) {
- DW.LNS_copy => {
- if (try prog.checkLineMatch()) |info| return info;
- prog.basic_block = false;
- },
- DW.LNS_advance_pc => {
- const arg = try leb.readULEB128(usize, &s.stream);
- prog.address += arg * minimum_instruction_length;
- },
- DW.LNS_advance_line => {
- const arg = try leb.readILEB128(i64, &s.stream);
- prog.line += arg;
- },
- DW.LNS_set_file => {
- const arg = try leb.readULEB128(usize, &s.stream);
- prog.file = arg;
- },
- DW.LNS_set_column => {
- const arg = try leb.readULEB128(u64, &s.stream);
- prog.column = arg;
- },
- DW.LNS_negate_stmt => {
- prog.is_stmt = !prog.is_stmt;
- },
- DW.LNS_set_basic_block => {
- prog.basic_block = true;
- },
- DW.LNS_const_add_pc => {
- const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
- prog.address += inc_addr;
- },
- DW.LNS_fixed_advance_pc => {
- const arg = try s.stream.readInt(u16, di.endian);
- prog.address += arg;
- },
- DW.LNS_set_prologue_end => {},
- else => {
- if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
- const len_bytes = standard_opcode_lengths[opcode - 1];
- try s.seekable_stream.seekBy(len_bytes);
- },
+ if (address >= seg_start and address < seg_end) {
+ if (self.address_map.getValue(seg_start)) |obj_di| {
+ return obj_di;
}
+
+ var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined;
+ // openFileAbsoluteW requires the prefix to be present
+ mem.copy(u16, name_buffer[0..4], &[_]u16{ '\\', '?', '?', '\\' });
+ const len = windows.kernel32.K32GetModuleFileNameExW(
+ process_handle,
+ module,
+ @ptrCast(windows.LPWSTR, &name_buffer[4]),
+ windows.PATH_MAX_WIDE,
+ );
+ assert(len > 0);
+
+ const obj_di = try self.allocator.create(ModuleDebugInfo);
+ errdefer self.allocator.destroy(obj_di);
+
+ obj_di.* = openCoffDebugInfo(self.allocator, name_buffer[0..:0]) catch |err| switch (err) {
+ error.FileNotFound => return error.MissingDebugInfo,
+ else => return err,
+ };
+ obj_di.base_address = seg_start;
+
+ try self.address_map.putNoClobber(seg_start, obj_di);
+
+ return obj_di;
}
}
return error.MissingDebugInfo;
}
- fn getString(di: *DwarfInfo, offset: u64) ![]u8 {
- if (offset > di.debug_str.len)
- return error.InvalidDebugInfo;
- const casted_offset = math.cast(usize, offset) catch
- return error.InvalidDebugInfo;
+ fn lookupModuleDl(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
+ var ctx: struct {
+ // Input
+ address: usize,
+ // Output
+ base_address: usize = undefined,
+ name: []const u8 = undefined,
+ } = .{ .address = address };
+ const CtxTy = @TypeOf(ctx);
- // Valid strings always have a terminating zero byte
- if (mem.indexOfScalarPos(u8, di.debug_str, casted_offset, 0)) |last| {
- return di.debug_str[casted_offset..last];
+ if (os.dl_iterate_phdr(&ctx, anyerror, struct {
+ fn callback(info: *os.dl_phdr_info, size: usize, context: *CtxTy) !void {
+ // The base address is too high
+ if (context.address < info.dlpi_addr)
+ return;
+
+ const phdrs = info.dlpi_phdr[0..info.dlpi_phnum];
+ for (phdrs) |*phdr| {
+ if (phdr.p_type != elf.PT_LOAD) continue;
+
+ const seg_start = info.dlpi_addr + phdr.p_vaddr;
+ const seg_end = seg_start + phdr.p_memsz;
+
+ if (context.address >= seg_start and context.address < seg_end) {
+ // Android libc uses NULL instead of an empty string to mark the
+ // main program
+ context.name = if (info.dlpi_name) |dlpi_name|
+ mem.toSliceConst(u8, dlpi_name)
+ else
+ "";
+ context.base_address = info.dlpi_addr;
+ // Stop the iteration
+ return error.Found;
+ }
+ }
+ }
+ }.callback)) {
+ return error.MissingDebugInfo;
+ } else |err| switch (err) {
+ error.Found => {},
+ else => return error.MissingDebugInfo,
}
- return error.InvalidDebugInfo;
+ if (self.address_map.getValue(ctx.base_address)) |obj_di| {
+ return obj_di;
+ }
+
+ const elf_path = if (ctx.name.len > 0)
+ ctx.name
+ else blk: {
+ var buf: [fs.MAX_PATH_BYTES]u8 = undefined;
+ break :blk try fs.selfExePath(&buf);
+ };
+
+ const obj_di = try self.allocator.create(ModuleDebugInfo);
+ errdefer self.allocator.destroy(obj_di);
+
+ obj_di.* = openElfDebugInfo(self.allocator, elf_path) catch |err| switch (err) {
+ error.FileNotFound => return error.MissingDebugInfo,
+ else => return err,
+ };
+ obj_di.base_address = ctx.base_address;
+
+ try self.address_map.putNoClobber(ctx.base_address, obj_di);
+
+ return obj_di;
}
};
-pub const DebugInfo = switch (builtin.os) {
+const SymbolInfo = struct {
+ symbol_name: []const u8 = "???",
+ compile_unit_name: []const u8 = "???",
+ line_info: ?LineInfo = null,
+
+ fn deinit(self: @This()) void {
+ if (self.line_info) |li| {
+ li.deinit();
+ }
+ }
+};
+
+pub const ModuleDebugInfo = switch (builtin.os.tag) {
.macosx, .ios, .watchos, .tvos => struct {
+ base_address: usize,
+ mapped_memory: []const u8,
symbols: []const MachoSymbol,
- strings: []const u8,
+ strings: [:0]const u8,
ofiles: OFileTable,
- const OFileTable = std.HashMap(
- *macho.nlist_64,
- DwarfInfo,
- std.hash_map.getHashPtrAddrFn(*macho.nlist_64),
- std.hash_map.getTrivialEqlFn(*macho.nlist_64),
- );
+ const OFileTable = std.StringHashMap(DW.DwarfInfo);
- pub fn allocator(self: DebugInfo) *mem.Allocator {
+ pub fn allocator(self: @This()) *mem.Allocator {
return self.ofiles.allocator;
}
+
+ fn loadOFile(self: *@This(), o_file_path: []const u8) !DW.DwarfInfo {
+ const mapped_mem = try mapWholeFile(o_file_path);
+
+ const hdr = @ptrCast(
+ *const macho.mach_header_64,
+ @alignCast(@alignOf(macho.mach_header_64), mapped_mem.ptr),
+ );
+ if (hdr.magic != std.macho.MH_MAGIC_64)
+ return error.InvalidDebugInfo;
+
+ const hdr_base = @ptrCast([*]const u8, hdr);
+ var ptr = hdr_base + @sizeOf(macho.mach_header_64);
+ var ncmd: u32 = hdr.ncmds;
+ const segcmd = while (ncmd != 0) : (ncmd -= 1) {
+ const lc = @ptrCast(*const std.macho.load_command, ptr);
+ switch (lc.cmd) {
+ std.macho.LC_SEGMENT_64 => {
+ break @ptrCast(
+ *const std.macho.segment_command_64,
+ @alignCast(@alignOf(std.macho.segment_command_64), ptr),
+ );
+ },
+ else => {},
+ }
+ ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);
+ } else {
+ return error.MissingDebugInfo;
+ };
+
+ var opt_debug_line: ?*const macho.section_64 = null;
+ var opt_debug_info: ?*const macho.section_64 = null;
+ var opt_debug_abbrev: ?*const macho.section_64 = null;
+ var opt_debug_str: ?*const macho.section_64 = null;
+ var opt_debug_ranges: ?*const macho.section_64 = null;
+
+ const sections = @ptrCast(
+ [*]const macho.section_64,
+ @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)),
+ )[0..segcmd.nsects];
+ for (sections) |*sect| {
+ // The section name may not exceed 16 chars and a trailing null may
+ // not be present
+ const name = if (mem.indexOfScalar(u8, sect.sectname[0..], 0)) |last|
+ sect.sectname[0..last]
+ else
+ sect.sectname[0..];
+
+ if (mem.eql(u8, name, "__debug_line")) {
+ opt_debug_line = sect;
+ } else if (mem.eql(u8, name, "__debug_info")) {
+ opt_debug_info = sect;
+ } else if (mem.eql(u8, name, "__debug_abbrev")) {
+ opt_debug_abbrev = sect;
+ } else if (mem.eql(u8, name, "__debug_str")) {
+ opt_debug_str = sect;
+ } else if (mem.eql(u8, name, "__debug_ranges")) {
+ opt_debug_ranges = sect;
+ }
+ }
+
+ const debug_line = opt_debug_line orelse
+ return error.MissingDebugInfo;
+ const debug_info = opt_debug_info orelse
+ return error.MissingDebugInfo;
+ const debug_str = opt_debug_str orelse
+ return error.MissingDebugInfo;
+ const debug_abbrev = opt_debug_abbrev orelse
+ return error.MissingDebugInfo;
+
+ var di = DW.DwarfInfo{
+ .endian = .Little,
+ .debug_info = try chopSlice(mapped_mem, debug_info.offset, debug_info.size),
+ .debug_abbrev = try chopSlice(mapped_mem, debug_abbrev.offset, debug_abbrev.size),
+ .debug_str = try chopSlice(mapped_mem, debug_str.offset, debug_str.size),
+ .debug_line = try chopSlice(mapped_mem, debug_line.offset, debug_line.size),
+ .debug_ranges = if (opt_debug_ranges) |debug_ranges|
+ try chopSlice(mapped_mem, debug_ranges.offset, debug_ranges.size)
+ else
+ null,
+ };
+
+ try DW.openDwarfDebugInfo(&di, self.allocator());
+
+ // Add the debug info to the cache
+ try self.ofiles.putNoClobber(o_file_path, di);
+
+ return di;
+ }
+
+ fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
+ // Translate the VA into an address into this object
+ const relocated_address = address - self.base_address;
+ assert(relocated_address >= 0x100000000);
+
+ // Find the .o file where this symbol is defined
+ const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse
+ return SymbolInfo{};
+
+ // XXX: Return the symbol name
+ if (symbol.ofile == null)
+ return SymbolInfo{};
+
+ assert(symbol.ofile.?.n_strx < self.strings.len);
+ const o_file_path = mem.toSliceConst(u8, self.strings.ptr + symbol.ofile.?.n_strx);
+
+ // Check if its debug infos are already in the cache
+ var o_file_di = self.ofiles.getValue(o_file_path) orelse
+ (self.loadOFile(o_file_path) catch |err| switch (err) {
+ error.MissingDebugInfo, error.InvalidDebugInfo => {
+ // XXX: Return the symbol name
+ return SymbolInfo{};
+ },
+ else => return err,
+ });
+
+ // Translate again the address, this time into an address inside the
+ // .o file
+ const relocated_address_o = relocated_address - symbol.reloc;
+
+ if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| {
+ return SymbolInfo{
+ .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???",
+ .compile_unit_name = compile_unit.die.getAttrString(&o_file_di, DW.AT_name) catch |err| switch (err) {
+ error.MissingDebugInfo, error.InvalidDebugInfo => "???",
+ else => return err,
+ },
+ .line_info = o_file_di.getLineNumberInfo(compile_unit.*, relocated_address_o) catch |err| switch (err) {
+ error.MissingDebugInfo, error.InvalidDebugInfo => null,
+ else => return err,
+ },
+ };
+ } else |err| switch (err) {
+ error.MissingDebugInfo, error.InvalidDebugInfo => {
+ return SymbolInfo{};
+ },
+ else => return err,
+ }
+
+ unreachable;
+ }
},
.uefi, .windows => struct {
+ base_address: usize,
pdb: pdb.Pdb,
coff: *coff.Coff,
sect_contribs: []pdb.SectionContribEntry,
modules: []Module,
- },
- else => DwarfInfo,
-};
-const PcRange = struct {
- start: u64,
- end: u64,
-};
-
-const CompileUnit = struct {
- version: u16,
- is_64: bool,
- die: *Die,
- pc_range: ?PcRange,
-};
-
-const AbbrevTable = ArrayList(AbbrevTableEntry);
-
-const AbbrevTableHeader = struct {
- // offset from .debug_abbrev
- offset: u64,
- table: AbbrevTable,
-};
-
-const AbbrevTableEntry = struct {
- has_children: bool,
- abbrev_code: u64,
- tag_id: u64,
- attrs: ArrayList(AbbrevAttr),
-};
-
-const AbbrevAttr = struct {
- attr_id: u64,
- form_id: u64,
-};
-
-const FormValue = union(enum) {
- Address: u64,
- Block: []u8,
- Const: Constant,
- ExprLoc: []u8,
- Flag: bool,
- SecOffset: u64,
- Ref: u64,
- RefAddr: u64,
- String: []u8,
- StrPtr: u64,
-};
-
-const Constant = struct {
- payload: u64,
- signed: bool,
-
- fn asUnsignedLe(self: *const Constant) !u64 {
- if (self.signed) return error.InvalidDebugInfo;
- return self.payload;
- }
-};
-
-const Die = struct {
- tag_id: u64,
- has_children: bool,
- attrs: ArrayList(Attr),
-
- const Attr = struct {
- id: u64,
- value: FormValue,
- };
-
- fn getAttr(self: *const Die, id: u64) ?*const FormValue {
- for (self.attrs.toSliceConst()) |*attr| {
- if (attr.id == id) return &attr.value;
+ pub fn allocator(self: @This()) *mem.Allocator {
+ return self.coff.allocator;
}
- return null;
- }
- fn getAttrAddr(self: *const Die, id: u64) !u64 {
- const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
- return switch (form_value.*) {
- FormValue.Address => |value| value,
- else => error.InvalidDebugInfo,
- };
- }
+ fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
+ // Translate the VA into an address into this object
+ const relocated_address = address - self.base_address;
- fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
- const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
- return switch (form_value.*) {
- FormValue.Const => |value| value.asUnsignedLe(),
- FormValue.SecOffset => |value| value,
- else => error.InvalidDebugInfo,
- };
- }
+ var coff_section: *coff.Section = undefined;
+ const mod_index = for (self.sect_contribs) |sect_contrib| {
+ if (sect_contrib.Section > self.coff.sections.len) continue;
+ // Remember that SectionContribEntry.Section is 1-based.
+ coff_section = &self.coff.sections.toSlice()[sect_contrib.Section - 1];
- fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
- const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
- return switch (form_value.*) {
- FormValue.Const => |value| value.asUnsignedLe(),
- else => error.InvalidDebugInfo,
- };
- }
+ const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;
+ const vaddr_end = vaddr_start + sect_contrib.Size;
+ if (relocated_address >= vaddr_start and relocated_address < vaddr_end) {
+ break sect_contrib.ModuleIndex;
+ }
+ } else {
+ // we have no information to add to the address
+ return SymbolInfo{};
+ };
- fn getAttrRef(self: *const Die, id: u64) !u64 {
- const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
- return switch (form_value.*) {
- FormValue.Ref => |value| value,
- else => error.InvalidDebugInfo,
- };
- }
+ const mod = &self.modules[mod_index];
+ try populateModule(self, mod);
+ const obj_basename = fs.path.basename(mod.obj_file_name);
- fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 {
- const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
- return switch (form_value.*) {
- FormValue.String => |value| value,
- FormValue.StrPtr => |offset| di.getString(offset),
- else => error.InvalidDebugInfo,
- };
- }
-};
+ var symbol_i: usize = 0;
+ const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) {
+ const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]);
+ if (prefix.RecordLen < 2)
+ return error.InvalidDebugInfo;
+ switch (prefix.RecordKind) {
+ .S_LPROC32, .S_GPROC32 => {
+ const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]);
+ const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset;
+ const vaddr_end = vaddr_start + proc_sym.CodeSize;
+ if (relocated_address >= vaddr_start and relocated_address < vaddr_end) {
+ break mem.toSliceConst(u8, @ptrCast([*:0]u8, proc_sym) + @sizeOf(pdb.ProcSym));
+ }
+ },
+ else => {},
+ }
+ symbol_i += prefix.RecordLen + @sizeOf(u16);
+ if (symbol_i > mod.symbols.len)
+ return error.InvalidDebugInfo;
+ } else "???";
-const FileEntry = struct {
- file_name: []const u8,
- dir_index: usize,
- mtime: usize,
- len_bytes: usize,
-};
+ const subsect_info = mod.subsect_info;
-pub const LineInfo = struct {
- line: u64,
- column: u64,
- file_name: []const u8,
- allocator: ?*mem.Allocator,
+ var sect_offset: usize = 0;
+ var skip_len: usize = undefined;
+ const opt_line_info = subsections: {
+ const checksum_offset = mod.checksum_offset orelse break :subsections null;
+ while (sect_offset != subsect_info.len) : (sect_offset += skip_len) {
+ const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]);
+ skip_len = subsect_hdr.Length;
+ sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
- fn deinit(self: LineInfo) void {
- const allocator = self.allocator orelse return;
- allocator.free(self.file_name);
- }
-};
+ switch (subsect_hdr.Kind) {
+ .Lines => {
+ var line_index = sect_offset;
-const LineNumberProgram = struct {
- address: usize,
- file: usize,
- line: i64,
- column: u64,
- is_stmt: bool,
- basic_block: bool,
- end_sequence: bool,
+ const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]);
+ if (line_hdr.RelocSegment == 0)
+ return error.MissingDebugInfo;
+ line_index += @sizeOf(pdb.LineFragmentHeader);
+ const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset;
+ const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize;
- default_is_stmt: bool,
- target_address: usize,
- include_dirs: []const []const u8,
- file_entries: *ArrayList(FileEntry),
+ if (relocated_address >= frag_vaddr_start and relocated_address < frag_vaddr_end) {
+ // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records)
+ // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in,
+ // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection.
+ const subsection_end_index = sect_offset + subsect_hdr.Length;
- prev_address: usize,
- prev_file: usize,
- prev_line: i64,
- prev_column: u64,
- prev_is_stmt: bool,
- prev_basic_block: bool,
- prev_end_sequence: bool,
+ while (line_index < subsection_end_index) {
+ const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]);
+ line_index += @sizeOf(pdb.LineBlockFragmentHeader);
+ const start_line_index = line_index;
- // Reset the state machine following the DWARF specification
- pub fn reset(self: *LineNumberProgram) void {
- self.address = 0;
- self.file = 1;
- self.line = 1;
- self.column = 0;
- self.is_stmt = self.default_is_stmt;
- self.basic_block = false;
- self.end_sequence = false;
- // Invalidate all the remaining fields
- self.prev_address = 0;
- self.prev_file = undefined;
- self.prev_line = undefined;
- self.prev_column = undefined;
- self.prev_is_stmt = undefined;
- self.prev_basic_block = undefined;
- self.prev_end_sequence = undefined;
- }
+ const has_column = line_hdr.Flags.LF_HaveColumns;
- pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram {
- return LineNumberProgram{
- .address = 0,
- .file = 1,
- .line = 1,
- .column = 0,
- .is_stmt = is_stmt,
- .basic_block = false,
- .end_sequence = false,
- .include_dirs = include_dirs,
- .file_entries = file_entries,
- .default_is_stmt = is_stmt,
- .target_address = target_address,
- .prev_address = 0,
- .prev_file = undefined,
- .prev_line = undefined,
- .prev_column = undefined,
- .prev_is_stmt = undefined,
- .prev_basic_block = undefined,
- .prev_end_sequence = undefined,
- };
- }
+ // All line entries are stored inside their line block by ascending start address.
+ // Heuristic: we want to find the last line entry
+ // that has a vaddr_start <= relocated_address.
+ // This is done with a simple linear search.
+ var line_i: u32 = 0;
+ while (line_i < block_hdr.NumLines) : (line_i += 1) {
+ const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]);
+ line_index += @sizeOf(pdb.LineNumberEntry);
- pub fn checkLineMatch(self: *LineNumberProgram) !?LineInfo {
- if (self.target_address >= self.prev_address and self.target_address < self.address) {
- const file_entry = if (self.prev_file == 0) {
- return error.MissingDebugInfo;
- } else if (self.prev_file - 1 >= self.file_entries.len) {
- return error.InvalidDebugInfo;
- } else
- &self.file_entries.items[self.prev_file - 1];
+ const vaddr_start = frag_vaddr_start + line_num_entry.Offset;
+ if (relocated_address < vaddr_start) {
+ break;
+ }
+ }
- const dir_name = if (file_entry.dir_index >= self.include_dirs.len) {
- return error.InvalidDebugInfo;
- } else
- self.include_dirs[file_entry.dir_index];
- const file_name = try fs.path.join(self.file_entries.allocator, &[_][]const u8{ dir_name, file_entry.file_name });
- errdefer self.file_entries.allocator.free(file_name);
- return LineInfo{
- .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0,
- .column = self.prev_column,
- .file_name = file_name,
- .allocator = self.file_entries.allocator,
+ // line_i == 0 would mean that no matching LineNumberEntry was found.
+ if (line_i > 0) {
+ const subsect_index = checksum_offset + block_hdr.NameIndex;
+ const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]);
+ const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset;
+ try self.pdb.string_table.seekTo(strtab_offset);
+ const source_file_name = try self.pdb.string_table.readNullTermString(self.allocator());
+
+ const line_entry_idx = line_i - 1;
+
+ const column = if (has_column) blk: {
+ const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines;
+ const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx;
+ const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]);
+ break :blk col_num_entry.StartColumn;
+ } else 0;
+
+ const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry);
+ const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]);
+ const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags);
+
+ break :subsections LineInfo{
+ .allocator = self.allocator(),
+ .file_name = source_file_name,
+ .line = flags.Start,
+ .column = column,
+ };
+ }
+ }
+
+ // Checking that we are not reading garbage after the (possibly) multiple block fragments.
+ if (line_index != subsection_end_index) {
+ return error.InvalidDebugInfo;
+ }
+ }
+ },
+ else => {},
+ }
+
+ if (sect_offset > subsect_info.len)
+ return error.InvalidDebugInfo;
+ } else {
+ break :subsections null;
+ }
+ };
+
+ return SymbolInfo{
+ .symbol_name = symbol_name,
+ .compile_unit_name = obj_basename,
+ .line_info = opt_line_info,
};
}
+ },
+ .linux, .freebsd => struct {
+ base_address: usize,
+ dwarf: DW.DwarfInfo,
+ mapped_memory: []const u8,
- self.prev_address = self.address;
- self.prev_file = self.file;
- self.prev_line = self.line;
- self.prev_column = self.column;
- self.prev_is_stmt = self.is_stmt;
- self.prev_basic_block = self.basic_block;
- self.prev_end_sequence = self.end_sequence;
- return null;
- }
-};
+ fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
+ // Translate the VA into an address into this object
+ const relocated_address = address - self.base_address;
-// TODO the noasyncs here are workarounds
-fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 {
- var buf = ArrayList(u8).init(allocator);
- while (true) {
- const byte = try noasync in_stream.readByte();
- if (byte == 0) break;
- try buf.append(byte);
- }
- return buf.toSlice();
-}
-
-// TODO the noasyncs here are workarounds
-fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 {
- const buf = try allocator.alloc(u8, size);
- errdefer allocator.free(buf);
- if ((try noasync in_stream.read(buf)) < size) return error.EndOfFile;
- return buf;
-}
-
-fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
- const buf = try readAllocBytes(allocator, in_stream, size);
- return FormValue{ .Block = buf };
-}
-
-// TODO the noasyncs here are workarounds
-fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
- const block_len = try noasync in_stream.readVarInt(usize, builtin.Endian.Little, size);
- return parseFormValueBlockLen(allocator, in_stream, block_len);
-}
-
-fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, comptime size: i32) !FormValue {
- // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here.
- // `noasync` should be removed from all the function calls once it is fixed.
- return FormValue{
- .Const = Constant{
- .signed = signed,
- .payload = switch (size) {
- 1 => try noasync in_stream.readIntLittle(u8),
- 2 => try noasync in_stream.readIntLittle(u16),
- 4 => try noasync in_stream.readIntLittle(u32),
- 8 => try noasync in_stream.readIntLittle(u64),
- -1 => blk: {
- if (signed) {
- const x = try noasync leb.readILEB128(i64, in_stream);
- break :blk @bitCast(u64, x);
- } else {
- const x = try noasync leb.readULEB128(u64, in_stream);
- break :blk x;
- }
+ if (noasync self.dwarf.findCompileUnit(relocated_address)) |compile_unit| {
+ return SymbolInfo{
+ .symbol_name = noasync self.dwarf.getSymbolName(relocated_address) orelse "???",
+ .compile_unit_name = compile_unit.die.getAttrString(&self.dwarf, DW.AT_name) catch |err| switch (err) {
+ error.MissingDebugInfo, error.InvalidDebugInfo => "???",
+ else => return err,
+ },
+ .line_info = noasync self.dwarf.getLineNumberInfo(compile_unit.*, relocated_address) catch |err| switch (err) {
+ error.MissingDebugInfo, error.InvalidDebugInfo => null,
+ else => return err,
+ },
+ };
+ } else |err| switch (err) {
+ error.MissingDebugInfo, error.InvalidDebugInfo => {
+ return SymbolInfo{};
},
- else => @compileError("Invalid size"),
- },
- },
- };
-}
-
-// TODO the noasyncs here are workarounds
-fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 {
- return if (is_64) try noasync in_stream.readIntLittle(u64) else @as(u64, try noasync in_stream.readIntLittle(u32));
-}
-
-// TODO the noasyncs here are workarounds
-fn parseFormValueTargetAddrSize(in_stream: var) !u64 {
- if (@sizeOf(usize) == 4) {
- // TODO this cast should not be needed
- return @as(u64, try noasync in_stream.readIntLittle(u32));
- } else if (@sizeOf(usize) == 8) {
- return noasync in_stream.readIntLittle(u64);
- } else {
- unreachable;
- }
-}
-
-// TODO the noasyncs here are workarounds
-fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, size: i32) !FormValue {
- return FormValue{
- .Ref = switch (size) {
- 1 => try noasync in_stream.readIntLittle(u8),
- 2 => try noasync in_stream.readIntLittle(u16),
- 4 => try noasync in_stream.readIntLittle(u32),
- 8 => try noasync in_stream.readIntLittle(u64),
- -1 => try noasync leb.readULEB128(u64, in_stream),
- else => unreachable,
- },
- };
-}
-
-// TODO the noasyncs here are workarounds
-fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue {
- return switch (form_id) {
- DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) },
- DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),
- DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2),
- DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4),
- DW.FORM_block => x: {
- const block_len = try noasync leb.readULEB128(usize, in_stream);
- return parseFormValueBlockLen(allocator, in_stream, block_len);
- },
- DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1),
- DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2),
- DW.FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4),
- DW.FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8),
- DW.FORM_udata, DW.FORM_sdata => {
- const signed = form_id == DW.FORM_sdata;
- return parseFormValueConstant(allocator, in_stream, signed, -1);
- },
- DW.FORM_exprloc => {
- const size = try noasync leb.readULEB128(usize, in_stream);
- const buf = try readAllocBytes(allocator, in_stream, size);
- return FormValue{ .ExprLoc = buf };
- },
- DW.FORM_flag => FormValue{ .Flag = (try noasync in_stream.readByte()) != 0 },
- DW.FORM_flag_present => FormValue{ .Flag = true },
- DW.FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
-
- DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, 1),
- DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, 2),
- DW.FORM_ref4 => parseFormValueRef(allocator, in_stream, 4),
- DW.FORM_ref8 => parseFormValueRef(allocator, in_stream, 8),
- DW.FORM_ref_udata => parseFormValueRef(allocator, in_stream, -1),
-
- DW.FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
- DW.FORM_ref_sig8 => FormValue{ .Ref = try noasync in_stream.readIntLittle(u64) },
-
- DW.FORM_string => FormValue{ .String = try readStringRaw(allocator, in_stream) },
- DW.FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
- DW.FORM_indirect => {
- const child_form_id = try noasync leb.readULEB128(u64, in_stream);
- const F = @TypeOf(async parseFormValue(allocator, in_stream, child_form_id, is_64));
- var frame = try allocator.create(F);
- defer allocator.destroy(frame);
- return await @asyncCall(frame, {}, parseFormValue, allocator, in_stream, child_form_id, is_64);
- },
- else => error.InvalidDebugInfo,
- };
-}
-
-fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry {
- for (abbrev_table.toSliceConst()) |*table_entry| {
- if (table_entry.abbrev_code == abbrev_code) return table_entry;
- }
- return null;
-}
-
-/// TODO resources https://github.com/ziglang/zig/issues/4353
-fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, address: usize) !LineInfo {
- const ofile = symbol.ofile orelse return error.MissingDebugInfo;
- const gop = try di.ofiles.getOrPut(ofile);
- const dwarf_info = if (gop.found_existing) &gop.kv.value else blk: {
- errdefer _ = di.ofiles.remove(ofile);
- const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx));
-
- var exe_file = try std.fs.openFileAbsoluteC(ofile_path, .{});
- errdefer exe_file.close();
-
- const exe_len = math.cast(usize, try exe_file.getEndPos()) catch
- return error.DebugInfoTooLarge;
- const exe_mmap = try os.mmap(
- null,
- exe_len,
- os.PROT_READ,
- os.MAP_SHARED,
- exe_file.handle,
- 0,
- );
- errdefer os.munmap(exe_mmap);
-
- const hdr = @ptrCast(
- *const macho.mach_header_64,
- @alignCast(@alignOf(macho.mach_header_64), exe_mmap.ptr),
- );
- if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo;
-
- const hdr_base = @ptrCast([*]const u8, hdr);
- var ptr = hdr_base + @sizeOf(macho.mach_header_64);
- var ncmd: u32 = hdr.ncmds;
- const segcmd = while (ncmd != 0) : (ncmd -= 1) {
- const lc = @ptrCast(*const std.macho.load_command, ptr);
- switch (lc.cmd) {
- std.macho.LC_SEGMENT_64 => {
- break @ptrCast(
- *const std.macho.segment_command_64,
- @alignCast(@alignOf(std.macho.segment_command_64), ptr),
- );
- },
- else => {},
+ else => return err,
}
- ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);
- } else {
- return error.MissingDebugInfo;
- };
- var opt_debug_line: ?*const macho.section_64 = null;
- var opt_debug_info: ?*const macho.section_64 = null;
- var opt_debug_abbrev: ?*const macho.section_64 = null;
- var opt_debug_str: ?*const macho.section_64 = null;
- var opt_debug_ranges: ?*const macho.section_64 = null;
-
- const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects];
- for (sections) |*sect| {
- // The section name may not exceed 16 chars and a trailing null may
- // not be present
- const name = if (mem.indexOfScalar(u8, sect.sectname[0..], 0)) |last|
- sect.sectname[0..last]
- else
- sect.sectname[0..];
-
- if (mem.eql(u8, name, "__debug_line")) {
- opt_debug_line = sect;
- } else if (mem.eql(u8, name, "__debug_info")) {
- opt_debug_info = sect;
- } else if (mem.eql(u8, name, "__debug_abbrev")) {
- opt_debug_abbrev = sect;
- } else if (mem.eql(u8, name, "__debug_str")) {
- opt_debug_str = sect;
- } else if (mem.eql(u8, name, "__debug_ranges")) {
- opt_debug_ranges = sect;
- }
+ unreachable;
}
-
- var debug_line = opt_debug_line orelse
- return error.MissingDebugInfo;
- var debug_info = opt_debug_info orelse
- return error.MissingDebugInfo;
- var debug_str = opt_debug_str orelse
- return error.MissingDebugInfo;
- var debug_abbrev = opt_debug_abbrev orelse
- return error.MissingDebugInfo;
-
- gop.kv.value = DwarfInfo{
- .endian = .Little,
- .debug_info = exe_mmap[@intCast(usize, debug_info.offset)..@intCast(usize, debug_info.offset + debug_info.size)],
- .debug_abbrev = exe_mmap[@intCast(usize, debug_abbrev.offset)..@intCast(usize, debug_abbrev.offset + debug_abbrev.size)],
- .debug_str = exe_mmap[@intCast(usize, debug_str.offset)..@intCast(usize, debug_str.offset + debug_str.size)],
- .debug_line = exe_mmap[@intCast(usize, debug_line.offset)..@intCast(usize, debug_line.offset + debug_line.size)],
- .debug_ranges = if (opt_debug_ranges) |debug_ranges|
- exe_mmap[@intCast(usize, debug_ranges.offset)..@intCast(usize, debug_ranges.offset + debug_ranges.size)]
- else
- null,
- };
- try openDwarfDebugInfo(&gop.kv.value, di.allocator());
-
- break :blk &gop.kv.value;
- };
-
- const o_file_address = address - symbol.reloc;
- const compile_unit = try dwarf_info.findCompileUnit(o_file_address);
- return dwarf_info.getLineNumberInfo(compile_unit.*, o_file_address);
-}
-
-const Func = struct {
- pc_range: ?PcRange,
- name: ?[]u8,
+ },
+ else => DW.DwarfInfo,
};
-fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
- const first_32_bits = try in_stream.readIntLittle(u32);
- is_64.* = (first_32_bits == 0xffffffff);
- if (is_64.*) {
- return in_stream.readIntLittle(u64);
- } else {
- if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
- // TODO this cast should not be needed
- return @as(u64, first_32_bits);
- }
-}
-
/// TODO multithreaded awareness
var debug_info_allocator: ?*mem.Allocator = null;
var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;
@@ -2225,7 +1602,7 @@ fn getDebugInfoAllocator() *mem.Allocator {
}
/// Whether or not the current target can print useful debug information when a segfault occurs.
-pub const have_segfault_handling_support = builtin.os == .linux or builtin.os == .windows;
+pub const have_segfault_handling_support = builtin.os.tag == .linux or builtin.os.tag == .windows;
pub const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler"))
root.enable_segfault_handler
else
@@ -2244,7 +1621,7 @@ pub fn attachSegfaultHandler() void {
if (!have_segfault_handling_support) {
@compileError("segfault handler not supported for this target");
}
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
return;
}
@@ -2260,7 +1637,7 @@ pub fn attachSegfaultHandler() void {
}
fn resetSegfaultHandler() void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
if (windows_segfault_handle) |handle| {
assert(windows.kernel32.RemoveVectoredExceptionHandler(handle) != 0);
windows_segfault_handle = null;
diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig
index 2f3b29302d..32a49b68e0 100644
--- a/lib/std/dwarf.zig
+++ b/lib/std/dwarf.zig
@@ -1,682 +1,891 @@
-pub const TAG_padding = 0x00;
-pub const TAG_array_type = 0x01;
-pub const TAG_class_type = 0x02;
-pub const TAG_entry_point = 0x03;
-pub const TAG_enumeration_type = 0x04;
-pub const TAG_formal_parameter = 0x05;
-pub const TAG_imported_declaration = 0x08;
-pub const TAG_label = 0x0a;
-pub const TAG_lexical_block = 0x0b;
-pub const TAG_member = 0x0d;
-pub const TAG_pointer_type = 0x0f;
-pub const TAG_reference_type = 0x10;
-pub const TAG_compile_unit = 0x11;
-pub const TAG_string_type = 0x12;
-pub const TAG_structure_type = 0x13;
-pub const TAG_subroutine = 0x14;
-pub const TAG_subroutine_type = 0x15;
-pub const TAG_typedef = 0x16;
-pub const TAG_union_type = 0x17;
-pub const TAG_unspecified_parameters = 0x18;
-pub const TAG_variant = 0x19;
-pub const TAG_common_block = 0x1a;
-pub const TAG_common_inclusion = 0x1b;
-pub const TAG_inheritance = 0x1c;
-pub const TAG_inlined_subroutine = 0x1d;
-pub const TAG_module = 0x1e;
-pub const TAG_ptr_to_member_type = 0x1f;
-pub const TAG_set_type = 0x20;
-pub const TAG_subrange_type = 0x21;
-pub const TAG_with_stmt = 0x22;
-pub const TAG_access_declaration = 0x23;
-pub const TAG_base_type = 0x24;
-pub const TAG_catch_block = 0x25;
-pub const TAG_const_type = 0x26;
-pub const TAG_constant = 0x27;
-pub const TAG_enumerator = 0x28;
-pub const TAG_file_type = 0x29;
-pub const TAG_friend = 0x2a;
-pub const TAG_namelist = 0x2b;
-pub const TAG_namelist_item = 0x2c;
-pub const TAG_packed_type = 0x2d;
-pub const TAG_subprogram = 0x2e;
-pub const TAG_template_type_param = 0x2f;
-pub const TAG_template_value_param = 0x30;
-pub const TAG_thrown_type = 0x31;
-pub const TAG_try_block = 0x32;
-pub const TAG_variant_part = 0x33;
-pub const TAG_variable = 0x34;
-pub const TAG_volatile_type = 0x35;
+const std = @import("std.zig");
+const builtin = @import("builtin");
+const debug = std.debug;
+const fs = std.fs;
+const io = std.io;
+const mem = std.mem;
+const math = std.math;
+const leb = @import("debug/leb128.zig");
-// DWARF 3
-pub const TAG_dwarf_procedure = 0x36;
-pub const TAG_restrict_type = 0x37;
-pub const TAG_interface_type = 0x38;
-pub const TAG_namespace = 0x39;
-pub const TAG_imported_module = 0x3a;
-pub const TAG_unspecified_type = 0x3b;
-pub const TAG_partial_unit = 0x3c;
-pub const TAG_imported_unit = 0x3d;
-pub const TAG_condition = 0x3f;
-pub const TAG_shared_type = 0x40;
+const ArrayList = std.ArrayList;
-// DWARF 4
-pub const TAG_type_unit = 0x41;
-pub const TAG_rvalue_reference_type = 0x42;
-pub const TAG_template_alias = 0x43;
+usingnamespace @import("dwarf_bits.zig");
-pub const TAG_lo_user = 0x4080;
-pub const TAG_hi_user = 0xffff;
+pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);
+pub const DwarfInStream = io.InStream(anyerror);
-// SGI/MIPS Extensions.
-pub const DW_TAG_MIPS_loop = 0x4081;
+const PcRange = struct {
+ start: u64,
+ end: u64,
+};
-// HP extensions. See: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz .
-pub const TAG_HP_array_descriptor = 0x4090;
-pub const TAG_HP_Bliss_field = 0x4091;
-pub const TAG_HP_Bliss_field_set = 0x4092;
+const Func = struct {
+ pc_range: ?PcRange,
+ name: ?[]const u8,
+};
-// GNU extensions.
-pub const TAG_format_label = 0x4101; // For FORTRAN 77 and Fortran 90.
-pub const TAG_function_template = 0x4102; // For C++.
-pub const TAG_class_template = 0x4103; //For C++.
-pub const TAG_GNU_BINCL = 0x4104;
-pub const TAG_GNU_EINCL = 0x4105;
+const CompileUnit = struct {
+ version: u16,
+ is_64: bool,
+ die: *Die,
+ pc_range: ?PcRange,
+};
-// Template template parameter.
-// See http://gcc.gnu.org/wiki/TemplateParmsDwarf .
-pub const TAG_GNU_template_template_param = 0x4106;
+const AbbrevTable = ArrayList(AbbrevTableEntry);
-// Template parameter pack extension = specified at
-// http://wiki.dwarfstd.org/index.php?title=C%2B%2B0x:_Variadic_templates
-// The values of these two TAGS are in the DW_TAG_GNU_* space until the tags
-// are properly part of DWARF 5.
-pub const TAG_GNU_template_parameter_pack = 0x4107;
-pub const TAG_GNU_formal_parameter_pack = 0x4108;
-// The GNU call site extension = specified at
-// http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open .
-// The values of these two TAGS are in the DW_TAG_GNU_* space until the tags
-// are properly part of DWARF 5.
-pub const TAG_GNU_call_site = 0x4109;
-pub const TAG_GNU_call_site_parameter = 0x410a;
-// Extensions for UPC. See: http://dwarfstd.org/doc/DWARF4.pdf.
-pub const TAG_upc_shared_type = 0x8765;
-pub const TAG_upc_strict_type = 0x8766;
-pub const TAG_upc_relaxed_type = 0x8767;
-// PGI (STMicroelectronics; extensions. No documentation available.
-pub const TAG_PGI_kanji_type = 0xA000;
-pub const TAG_PGI_interface_block = 0xA020;
+const AbbrevTableHeader = struct {
+ // offset from .debug_abbrev
+ offset: u64,
+ table: AbbrevTable,
+};
-pub const FORM_addr = 0x01;
-pub const FORM_block2 = 0x03;
-pub const FORM_block4 = 0x04;
-pub const FORM_data2 = 0x05;
-pub const FORM_data4 = 0x06;
-pub const FORM_data8 = 0x07;
-pub const FORM_string = 0x08;
-pub const FORM_block = 0x09;
-pub const FORM_block1 = 0x0a;
-pub const FORM_data1 = 0x0b;
-pub const FORM_flag = 0x0c;
-pub const FORM_sdata = 0x0d;
-pub const FORM_strp = 0x0e;
-pub const FORM_udata = 0x0f;
-pub const FORM_ref_addr = 0x10;
-pub const FORM_ref1 = 0x11;
-pub const FORM_ref2 = 0x12;
-pub const FORM_ref4 = 0x13;
-pub const FORM_ref8 = 0x14;
-pub const FORM_ref_udata = 0x15;
-pub const FORM_indirect = 0x16;
-pub const FORM_sec_offset = 0x17;
-pub const FORM_exprloc = 0x18;
-pub const FORM_flag_present = 0x19;
-pub const FORM_ref_sig8 = 0x20;
+const AbbrevTableEntry = struct {
+ has_children: bool,
+ abbrev_code: u64,
+ tag_id: u64,
+ attrs: ArrayList(AbbrevAttr),
+};
-// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
-pub const FORM_GNU_addr_index = 0x1f01;
-pub const FORM_GNU_str_index = 0x1f02;
+const AbbrevAttr = struct {
+ attr_id: u64,
+ form_id: u64,
+};
-// Extensions for DWZ multifile.
-// See http://www.dwarfstd.org/ShowIssue.php?issue=120604.1&type=open .
-pub const FORM_GNU_ref_alt = 0x1f20;
-pub const FORM_GNU_strp_alt = 0x1f21;
+const FormValue = union(enum) {
+ Address: u64,
+ Block: []u8,
+ Const: Constant,
+ ExprLoc: []u8,
+ Flag: bool,
+ SecOffset: u64,
+ Ref: u64,
+ RefAddr: u64,
+ String: []const u8,
+ StrPtr: u64,
+};
-pub const AT_sibling = 0x01;
-pub const AT_location = 0x02;
-pub const AT_name = 0x03;
-pub const AT_ordering = 0x09;
-pub const AT_subscr_data = 0x0a;
-pub const AT_byte_size = 0x0b;
-pub const AT_bit_offset = 0x0c;
-pub const AT_bit_size = 0x0d;
-pub const AT_element_list = 0x0f;
-pub const AT_stmt_list = 0x10;
-pub const AT_low_pc = 0x11;
-pub const AT_high_pc = 0x12;
-pub const AT_language = 0x13;
-pub const AT_member = 0x14;
-pub const AT_discr = 0x15;
-pub const AT_discr_value = 0x16;
-pub const AT_visibility = 0x17;
-pub const AT_import = 0x18;
-pub const AT_string_length = 0x19;
-pub const AT_common_reference = 0x1a;
-pub const AT_comp_dir = 0x1b;
-pub const AT_const_value = 0x1c;
-pub const AT_containing_type = 0x1d;
-pub const AT_default_value = 0x1e;
-pub const AT_inline = 0x20;
-pub const AT_is_optional = 0x21;
-pub const AT_lower_bound = 0x22;
-pub const AT_producer = 0x25;
-pub const AT_prototyped = 0x27;
-pub const AT_return_addr = 0x2a;
-pub const AT_start_scope = 0x2c;
-pub const AT_bit_stride = 0x2e;
-pub const AT_upper_bound = 0x2f;
-pub const AT_abstract_origin = 0x31;
-pub const AT_accessibility = 0x32;
-pub const AT_address_class = 0x33;
-pub const AT_artificial = 0x34;
-pub const AT_base_types = 0x35;
-pub const AT_calling_convention = 0x36;
-pub const AT_count = 0x37;
-pub const AT_data_member_location = 0x38;
-pub const AT_decl_column = 0x39;
-pub const AT_decl_file = 0x3a;
-pub const AT_decl_line = 0x3b;
-pub const AT_declaration = 0x3c;
-pub const AT_discr_list = 0x3d;
-pub const AT_encoding = 0x3e;
-pub const AT_external = 0x3f;
-pub const AT_frame_base = 0x40;
-pub const AT_friend = 0x41;
-pub const AT_identifier_case = 0x42;
-pub const AT_macro_info = 0x43;
-pub const AT_namelist_items = 0x44;
-pub const AT_priority = 0x45;
-pub const AT_segment = 0x46;
-pub const AT_specification = 0x47;
-pub const AT_static_link = 0x48;
-pub const AT_type = 0x49;
-pub const AT_use_location = 0x4a;
-pub const AT_variable_parameter = 0x4b;
-pub const AT_virtuality = 0x4c;
-pub const AT_vtable_elem_location = 0x4d;
+const Constant = struct {
+ payload: u64,
+ signed: bool,
-// DWARF 3 values.
-pub const AT_allocated = 0x4e;
-pub const AT_associated = 0x4f;
-pub const AT_data_location = 0x50;
-pub const AT_byte_stride = 0x51;
-pub const AT_entry_pc = 0x52;
-pub const AT_use_UTF8 = 0x53;
-pub const AT_extension = 0x54;
-pub const AT_ranges = 0x55;
-pub const AT_trampoline = 0x56;
-pub const AT_call_column = 0x57;
-pub const AT_call_file = 0x58;
-pub const AT_call_line = 0x59;
-pub const AT_description = 0x5a;
-pub const AT_binary_scale = 0x5b;
-pub const AT_decimal_scale = 0x5c;
-pub const AT_small = 0x5d;
-pub const AT_decimal_sign = 0x5e;
-pub const AT_digit_count = 0x5f;
-pub const AT_picture_string = 0x60;
-pub const AT_mutable = 0x61;
-pub const AT_threads_scaled = 0x62;
-pub const AT_explicit = 0x63;
-pub const AT_object_pointer = 0x64;
-pub const AT_endianity = 0x65;
-pub const AT_elemental = 0x66;
-pub const AT_pure = 0x67;
-pub const AT_recursive = 0x68;
+ fn asUnsignedLe(self: *const Constant) !u64 {
+ if (self.signed) return error.InvalidDebugInfo;
+ return self.payload;
+ }
+};
-// DWARF 4.
-pub const AT_signature = 0x69;
-pub const AT_main_subprogram = 0x6a;
-pub const AT_data_bit_offset = 0x6b;
-pub const AT_const_expr = 0x6c;
-pub const AT_enum_class = 0x6d;
-pub const AT_linkage_name = 0x6e;
+const Die = struct {
+ tag_id: u64,
+ has_children: bool,
+ attrs: ArrayList(Attr),
-// DWARF 5
-pub const AT_alignment = 0x88;
+ const Attr = struct {
+ id: u64,
+ value: FormValue,
+ };
-pub const AT_lo_user = 0x2000; // Implementation-defined range start.
-pub const AT_hi_user = 0x3fff; // Implementation-defined range end.
+ fn getAttr(self: *const Die, id: u64) ?*const FormValue {
+ for (self.attrs.toSliceConst()) |*attr| {
+ if (attr.id == id) return &attr.value;
+ }
+ return null;
+ }
-// SGI/MIPS extensions.
-pub const AT_MIPS_fde = 0x2001;
-pub const AT_MIPS_loop_begin = 0x2002;
-pub const AT_MIPS_tail_loop_begin = 0x2003;
-pub const AT_MIPS_epilog_begin = 0x2004;
-pub const AT_MIPS_loop_unroll_factor = 0x2005;
-pub const AT_MIPS_software_pipeline_depth = 0x2006;
-pub const AT_MIPS_linkage_name = 0x2007;
-pub const AT_MIPS_stride = 0x2008;
-pub const AT_MIPS_abstract_name = 0x2009;
-pub const AT_MIPS_clone_origin = 0x200a;
-pub const AT_MIPS_has_inlines = 0x200b;
+ fn getAttrAddr(self: *const Die, id: u64) !u64 {
+ const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
+ return switch (form_value.*) {
+ FormValue.Address => |value| value,
+ else => error.InvalidDebugInfo,
+ };
+ }
-// HP extensions.
-pub const AT_HP_block_index = 0x2000;
-pub const AT_HP_unmodifiable = 0x2001; // Same as DW_AT_MIPS_fde.
-pub const AT_HP_prologue = 0x2005; // Same as DW_AT_MIPS_loop_unroll.
-pub const AT_HP_epilogue = 0x2008; // Same as DW_AT_MIPS_stride.
-pub const AT_HP_actuals_stmt_list = 0x2010;
-pub const AT_HP_proc_per_section = 0x2011;
-pub const AT_HP_raw_data_ptr = 0x2012;
-pub const AT_HP_pass_by_reference = 0x2013;
-pub const AT_HP_opt_level = 0x2014;
-pub const AT_HP_prof_version_id = 0x2015;
-pub const AT_HP_opt_flags = 0x2016;
-pub const AT_HP_cold_region_low_pc = 0x2017;
-pub const AT_HP_cold_region_high_pc = 0x2018;
-pub const AT_HP_all_variables_modifiable = 0x2019;
-pub const AT_HP_linkage_name = 0x201a;
-pub const AT_HP_prof_flags = 0x201b; // In comp unit of procs_info for -g.
-pub const AT_HP_unit_name = 0x201f;
-pub const AT_HP_unit_size = 0x2020;
-pub const AT_HP_widened_byte_size = 0x2021;
-pub const AT_HP_definition_points = 0x2022;
-pub const AT_HP_default_location = 0x2023;
-pub const AT_HP_is_result_param = 0x2029;
+ fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
+ const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
+ return switch (form_value.*) {
+ FormValue.Const => |value| value.asUnsignedLe(),
+ FormValue.SecOffset => |value| value,
+ else => error.InvalidDebugInfo,
+ };
+ }
-// GNU extensions.
-pub const AT_sf_names = 0x2101;
-pub const AT_src_info = 0x2102;
-pub const AT_mac_info = 0x2103;
-pub const AT_src_coords = 0x2104;
-pub const AT_body_begin = 0x2105;
-pub const AT_body_end = 0x2106;
-pub const AT_GNU_vector = 0x2107;
-// Thread-safety annotations.
-// See http://gcc.gnu.org/wiki/ThreadSafetyAnnotation .
-pub const AT_GNU_guarded_by = 0x2108;
-pub const AT_GNU_pt_guarded_by = 0x2109;
-pub const AT_GNU_guarded = 0x210a;
-pub const AT_GNU_pt_guarded = 0x210b;
-pub const AT_GNU_locks_excluded = 0x210c;
-pub const AT_GNU_exclusive_locks_required = 0x210d;
-pub const AT_GNU_shared_locks_required = 0x210e;
-// One-definition rule violation detection.
-// See http://gcc.gnu.org/wiki/DwarfSeparateTypeInfo .
-pub const AT_GNU_odr_signature = 0x210f;
-// Template template argument name.
-// See http://gcc.gnu.org/wiki/TemplateParmsDwarf .
-pub const AT_GNU_template_name = 0x2110;
-// The GNU call site extension.
-// See http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open .
-pub const AT_GNU_call_site_value = 0x2111;
-pub const AT_GNU_call_site_data_value = 0x2112;
-pub const AT_GNU_call_site_target = 0x2113;
-pub const AT_GNU_call_site_target_clobbered = 0x2114;
-pub const AT_GNU_tail_call = 0x2115;
-pub const AT_GNU_all_tail_call_sites = 0x2116;
-pub const AT_GNU_all_call_sites = 0x2117;
-pub const AT_GNU_all_source_call_sites = 0x2118;
-// Section offset into .debug_macro section.
-pub const AT_GNU_macros = 0x2119;
-// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
-pub const AT_GNU_dwo_name = 0x2130;
-pub const AT_GNU_dwo_id = 0x2131;
-pub const AT_GNU_ranges_base = 0x2132;
-pub const AT_GNU_addr_base = 0x2133;
-pub const AT_GNU_pubnames = 0x2134;
-pub const AT_GNU_pubtypes = 0x2135;
-// VMS extensions.
-pub const AT_VMS_rtnbeg_pd_address = 0x2201;
-// GNAT extensions.
-// GNAT descriptive type.
-// See http://gcc.gnu.org/wiki/DW_AT_GNAT_descriptive_type .
-pub const AT_use_GNAT_descriptive_type = 0x2301;
-pub const AT_GNAT_descriptive_type = 0x2302;
-// UPC extension.
-pub const AT_upc_threads_scaled = 0x3210;
-// PGI (STMicroelectronics) extensions.
-pub const AT_PGI_lbase = 0x3a00;
-pub const AT_PGI_soffset = 0x3a01;
-pub const AT_PGI_lstride = 0x3a02;
+ fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
+ const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
+ return switch (form_value.*) {
+ FormValue.Const => |value| value.asUnsignedLe(),
+ else => error.InvalidDebugInfo,
+ };
+ }
-pub const OP_addr = 0x03;
-pub const OP_deref = 0x06;
-pub const OP_const1u = 0x08;
-pub const OP_const1s = 0x09;
-pub const OP_const2u = 0x0a;
-pub const OP_const2s = 0x0b;
-pub const OP_const4u = 0x0c;
-pub const OP_const4s = 0x0d;
-pub const OP_const8u = 0x0e;
-pub const OP_const8s = 0x0f;
-pub const OP_constu = 0x10;
-pub const OP_consts = 0x11;
-pub const OP_dup = 0x12;
-pub const OP_drop = 0x13;
-pub const OP_over = 0x14;
-pub const OP_pick = 0x15;
-pub const OP_swap = 0x16;
-pub const OP_rot = 0x17;
-pub const OP_xderef = 0x18;
-pub const OP_abs = 0x19;
-pub const OP_and = 0x1a;
-pub const OP_div = 0x1b;
-pub const OP_minus = 0x1c;
-pub const OP_mod = 0x1d;
-pub const OP_mul = 0x1e;
-pub const OP_neg = 0x1f;
-pub const OP_not = 0x20;
-pub const OP_or = 0x21;
-pub const OP_plus = 0x22;
-pub const OP_plus_uconst = 0x23;
-pub const OP_shl = 0x24;
-pub const OP_shr = 0x25;
-pub const OP_shra = 0x26;
-pub const OP_xor = 0x27;
-pub const OP_bra = 0x28;
-pub const OP_eq = 0x29;
-pub const OP_ge = 0x2a;
-pub const OP_gt = 0x2b;
-pub const OP_le = 0x2c;
-pub const OP_lt = 0x2d;
-pub const OP_ne = 0x2e;
-pub const OP_skip = 0x2f;
-pub const OP_lit0 = 0x30;
-pub const OP_lit1 = 0x31;
-pub const OP_lit2 = 0x32;
-pub const OP_lit3 = 0x33;
-pub const OP_lit4 = 0x34;
-pub const OP_lit5 = 0x35;
-pub const OP_lit6 = 0x36;
-pub const OP_lit7 = 0x37;
-pub const OP_lit8 = 0x38;
-pub const OP_lit9 = 0x39;
-pub const OP_lit10 = 0x3a;
-pub const OP_lit11 = 0x3b;
-pub const OP_lit12 = 0x3c;
-pub const OP_lit13 = 0x3d;
-pub const OP_lit14 = 0x3e;
-pub const OP_lit15 = 0x3f;
-pub const OP_lit16 = 0x40;
-pub const OP_lit17 = 0x41;
-pub const OP_lit18 = 0x42;
-pub const OP_lit19 = 0x43;
-pub const OP_lit20 = 0x44;
-pub const OP_lit21 = 0x45;
-pub const OP_lit22 = 0x46;
-pub const OP_lit23 = 0x47;
-pub const OP_lit24 = 0x48;
-pub const OP_lit25 = 0x49;
-pub const OP_lit26 = 0x4a;
-pub const OP_lit27 = 0x4b;
-pub const OP_lit28 = 0x4c;
-pub const OP_lit29 = 0x4d;
-pub const OP_lit30 = 0x4e;
-pub const OP_lit31 = 0x4f;
-pub const OP_reg0 = 0x50;
-pub const OP_reg1 = 0x51;
-pub const OP_reg2 = 0x52;
-pub const OP_reg3 = 0x53;
-pub const OP_reg4 = 0x54;
-pub const OP_reg5 = 0x55;
-pub const OP_reg6 = 0x56;
-pub const OP_reg7 = 0x57;
-pub const OP_reg8 = 0x58;
-pub const OP_reg9 = 0x59;
-pub const OP_reg10 = 0x5a;
-pub const OP_reg11 = 0x5b;
-pub const OP_reg12 = 0x5c;
-pub const OP_reg13 = 0x5d;
-pub const OP_reg14 = 0x5e;
-pub const OP_reg15 = 0x5f;
-pub const OP_reg16 = 0x60;
-pub const OP_reg17 = 0x61;
-pub const OP_reg18 = 0x62;
-pub const OP_reg19 = 0x63;
-pub const OP_reg20 = 0x64;
-pub const OP_reg21 = 0x65;
-pub const OP_reg22 = 0x66;
-pub const OP_reg23 = 0x67;
-pub const OP_reg24 = 0x68;
-pub const OP_reg25 = 0x69;
-pub const OP_reg26 = 0x6a;
-pub const OP_reg27 = 0x6b;
-pub const OP_reg28 = 0x6c;
-pub const OP_reg29 = 0x6d;
-pub const OP_reg30 = 0x6e;
-pub const OP_reg31 = 0x6f;
-pub const OP_breg0 = 0x70;
-pub const OP_breg1 = 0x71;
-pub const OP_breg2 = 0x72;
-pub const OP_breg3 = 0x73;
-pub const OP_breg4 = 0x74;
-pub const OP_breg5 = 0x75;
-pub const OP_breg6 = 0x76;
-pub const OP_breg7 = 0x77;
-pub const OP_breg8 = 0x78;
-pub const OP_breg9 = 0x79;
-pub const OP_breg10 = 0x7a;
-pub const OP_breg11 = 0x7b;
-pub const OP_breg12 = 0x7c;
-pub const OP_breg13 = 0x7d;
-pub const OP_breg14 = 0x7e;
-pub const OP_breg15 = 0x7f;
-pub const OP_breg16 = 0x80;
-pub const OP_breg17 = 0x81;
-pub const OP_breg18 = 0x82;
-pub const OP_breg19 = 0x83;
-pub const OP_breg20 = 0x84;
-pub const OP_breg21 = 0x85;
-pub const OP_breg22 = 0x86;
-pub const OP_breg23 = 0x87;
-pub const OP_breg24 = 0x88;
-pub const OP_breg25 = 0x89;
-pub const OP_breg26 = 0x8a;
-pub const OP_breg27 = 0x8b;
-pub const OP_breg28 = 0x8c;
-pub const OP_breg29 = 0x8d;
-pub const OP_breg30 = 0x8e;
-pub const OP_breg31 = 0x8f;
-pub const OP_regx = 0x90;
-pub const OP_fbreg = 0x91;
-pub const OP_bregx = 0x92;
-pub const OP_piece = 0x93;
-pub const OP_deref_size = 0x94;
-pub const OP_xderef_size = 0x95;
-pub const OP_nop = 0x96;
+ fn getAttrRef(self: *const Die, id: u64) !u64 {
+ const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
+ return switch (form_value.*) {
+ FormValue.Ref => |value| value,
+ else => error.InvalidDebugInfo,
+ };
+ }
-// DWARF 3 extensions.
-pub const OP_push_object_address = 0x97;
-pub const OP_call2 = 0x98;
-pub const OP_call4 = 0x99;
-pub const OP_call_ref = 0x9a;
-pub const OP_form_tls_address = 0x9b;
-pub const OP_call_frame_cfa = 0x9c;
-pub const OP_bit_piece = 0x9d;
+ fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]const u8 {
+ const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
+ return switch (form_value.*) {
+ FormValue.String => |value| value,
+ FormValue.StrPtr => |offset| di.getString(offset),
+ else => error.InvalidDebugInfo,
+ };
+ }
+};
-// DWARF 4 extensions.
-pub const OP_implicit_value = 0x9e;
-pub const OP_stack_value = 0x9f;
+const FileEntry = struct {
+ file_name: []const u8,
+ dir_index: usize,
+ mtime: usize,
+ len_bytes: usize,
+};
-pub const OP_lo_user = 0xe0; // Implementation-defined range start.
-pub const OP_hi_user = 0xff; // Implementation-defined range end.
+const LineNumberProgram = struct {
+ address: usize,
+ file: usize,
+ line: i64,
+ column: u64,
+ is_stmt: bool,
+ basic_block: bool,
+ end_sequence: bool,
-// GNU extensions.
-pub const OP_GNU_push_tls_address = 0xe0;
-// The following is for marking variables that are uninitialized.
-pub const OP_GNU_uninit = 0xf0;
-pub const OP_GNU_encoded_addr = 0xf1;
-// The GNU implicit pointer extension.
-// See http://www.dwarfstd.org/ShowIssue.php?issue=100831.1&type=open .
-pub const OP_GNU_implicit_pointer = 0xf2;
-// The GNU entry value extension.
-// See http://www.dwarfstd.org/ShowIssue.php?issue=100909.1&type=open .
-pub const OP_GNU_entry_value = 0xf3;
-// The GNU typed stack extension.
-// See http://www.dwarfstd.org/doc/040408.1.html .
-pub const OP_GNU_const_type = 0xf4;
-pub const OP_GNU_regval_type = 0xf5;
-pub const OP_GNU_deref_type = 0xf6;
-pub const OP_GNU_convert = 0xf7;
-pub const OP_GNU_reinterpret = 0xf9;
-// The GNU parameter ref extension.
-pub const OP_GNU_parameter_ref = 0xfa;
-// Extension for Fission. See http://gcc.gnu.org/wiki/DebugFission.
-pub const OP_GNU_addr_index = 0xfb;
-pub const OP_GNU_const_index = 0xfc;
-// HP extensions.
-pub const OP_HP_unknown = 0xe0; // Ouch, the same as GNU_push_tls_address.
-pub const OP_HP_is_value = 0xe1;
-pub const OP_HP_fltconst4 = 0xe2;
-pub const OP_HP_fltconst8 = 0xe3;
-pub const OP_HP_mod_range = 0xe4;
-pub const OP_HP_unmod_range = 0xe5;
-pub const OP_HP_tls = 0xe6;
-// PGI (STMicroelectronics) extensions.
-pub const OP_PGI_omp_thread_num = 0xf8;
+ default_is_stmt: bool,
+ target_address: usize,
+ include_dirs: []const []const u8,
+ file_entries: *ArrayList(FileEntry),
-pub const ATE_void = 0x0;
-pub const ATE_address = 0x1;
-pub const ATE_boolean = 0x2;
-pub const ATE_complex_float = 0x3;
-pub const ATE_float = 0x4;
-pub const ATE_signed = 0x5;
-pub const ATE_signed_char = 0x6;
-pub const ATE_unsigned = 0x7;
-pub const ATE_unsigned_char = 0x8;
+ prev_address: usize,
+ prev_file: usize,
+ prev_line: i64,
+ prev_column: u64,
+ prev_is_stmt: bool,
+ prev_basic_block: bool,
+ prev_end_sequence: bool,
-// DWARF 3.
-pub const ATE_imaginary_float = 0x9;
-pub const ATE_packed_decimal = 0xa;
-pub const ATE_numeric_string = 0xb;
-pub const ATE_edited = 0xc;
-pub const ATE_signed_fixed = 0xd;
-pub const ATE_unsigned_fixed = 0xe;
-pub const ATE_decimal_float = 0xf;
+ // Reset the state machine following the DWARF specification
+ pub fn reset(self: *LineNumberProgram) void {
+ self.address = 0;
+ self.file = 1;
+ self.line = 1;
+ self.column = 0;
+ self.is_stmt = self.default_is_stmt;
+ self.basic_block = false;
+ self.end_sequence = false;
+ // Invalidate all the remaining fields
+ self.prev_address = 0;
+ self.prev_file = undefined;
+ self.prev_line = undefined;
+ self.prev_column = undefined;
+ self.prev_is_stmt = undefined;
+ self.prev_basic_block = undefined;
+ self.prev_end_sequence = undefined;
+ }
-// DWARF 4.
-pub const ATE_UTF = 0x10;
+ pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram {
+ return LineNumberProgram{
+ .address = 0,
+ .file = 1,
+ .line = 1,
+ .column = 0,
+ .is_stmt = is_stmt,
+ .basic_block = false,
+ .end_sequence = false,
+ .include_dirs = include_dirs,
+ .file_entries = file_entries,
+ .default_is_stmt = is_stmt,
+ .target_address = target_address,
+ .prev_address = 0,
+ .prev_file = undefined,
+ .prev_line = undefined,
+ .prev_column = undefined,
+ .prev_is_stmt = undefined,
+ .prev_basic_block = undefined,
+ .prev_end_sequence = undefined,
+ };
+ }
-pub const ATE_lo_user = 0x80;
-pub const ATE_hi_user = 0xff;
+ pub fn checkLineMatch(self: *LineNumberProgram) !?debug.LineInfo {
+ if (self.target_address >= self.prev_address and self.target_address < self.address) {
+ const file_entry = if (self.prev_file == 0) {
+ return error.MissingDebugInfo;
+ } else if (self.prev_file - 1 >= self.file_entries.len) {
+ return error.InvalidDebugInfo;
+ } else
+ &self.file_entries.items[self.prev_file - 1];
-// HP extensions.
-pub const ATE_HP_float80 = 0x80; // Floating-point (80 bit).
-pub const ATE_HP_complex_float80 = 0x81; // Complex floating-point (80 bit).
-pub const ATE_HP_float128 = 0x82; // Floating-point (128 bit).
-pub const ATE_HP_complex_float128 = 0x83; // Complex fp (128 bit).
-pub const ATE_HP_floathpintel = 0x84; // Floating-point (82 bit IA64).
-pub const ATE_HP_imaginary_float80 = 0x85;
-pub const ATE_HP_imaginary_float128 = 0x86;
-pub const ATE_HP_VAX_float = 0x88; // F or G floating.
-pub const ATE_HP_VAX_float_d = 0x89; // D floating.
-pub const ATE_HP_packed_decimal = 0x8a; // Cobol.
-pub const ATE_HP_zoned_decimal = 0x8b; // Cobol.
-pub const ATE_HP_edited = 0x8c; // Cobol.
-pub const ATE_HP_signed_fixed = 0x8d; // Cobol.
-pub const ATE_HP_unsigned_fixed = 0x8e; // Cobol.
-pub const ATE_HP_VAX_complex_float = 0x8f; // F or G floating complex.
-pub const ATE_HP_VAX_complex_float_d = 0x90; // D floating complex.
+ const dir_name = if (file_entry.dir_index >= self.include_dirs.len) {
+ return error.InvalidDebugInfo;
+ } else
+ self.include_dirs[file_entry.dir_index];
+ const file_name = try fs.path.join(self.file_entries.allocator, &[_][]const u8{ dir_name, file_entry.file_name });
+ errdefer self.file_entries.allocator.free(file_name);
+ return debug.LineInfo{
+ .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0,
+ .column = self.prev_column,
+ .file_name = file_name,
+ .allocator = self.file_entries.allocator,
+ };
+ }
-pub const CFA_advance_loc = 0x40;
-pub const CFA_offset = 0x80;
-pub const CFA_restore = 0xc0;
-pub const CFA_nop = 0x00;
-pub const CFA_set_loc = 0x01;
-pub const CFA_advance_loc1 = 0x02;
-pub const CFA_advance_loc2 = 0x03;
-pub const CFA_advance_loc4 = 0x04;
-pub const CFA_offset_extended = 0x05;
-pub const CFA_restore_extended = 0x06;
-pub const CFA_undefined = 0x07;
-pub const CFA_same_value = 0x08;
-pub const CFA_register = 0x09;
-pub const CFA_remember_state = 0x0a;
-pub const CFA_restore_state = 0x0b;
-pub const CFA_def_cfa = 0x0c;
-pub const CFA_def_cfa_register = 0x0d;
-pub const CFA_def_cfa_offset = 0x0e;
+ self.prev_address = self.address;
+ self.prev_file = self.file;
+ self.prev_line = self.line;
+ self.prev_column = self.column;
+ self.prev_is_stmt = self.is_stmt;
+ self.prev_basic_block = self.basic_block;
+ self.prev_end_sequence = self.end_sequence;
+ return null;
+ }
+};
-// DWARF 3.
-pub const CFA_def_cfa_expression = 0x0f;
-pub const CFA_expression = 0x10;
-pub const CFA_offset_extended_sf = 0x11;
-pub const CFA_def_cfa_sf = 0x12;
-pub const CFA_def_cfa_offset_sf = 0x13;
-pub const CFA_val_offset = 0x14;
-pub const CFA_val_offset_sf = 0x15;
-pub const CFA_val_expression = 0x16;
+fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
+ const first_32_bits = try in_stream.readIntLittle(u32);
+ is_64.* = (first_32_bits == 0xffffffff);
+ if (is_64.*) {
+ return in_stream.readIntLittle(u64);
+ } else {
+ if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
+ // TODO this cast should not be needed
+ return @as(u64, first_32_bits);
+ }
+}
-pub const CFA_lo_user = 0x1c;
-pub const CFA_hi_user = 0x3f;
+// TODO the noasyncs here are workarounds
+fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 {
+ const buf = try allocator.alloc(u8, size);
+ errdefer allocator.free(buf);
+ if ((try noasync in_stream.read(buf)) < size) return error.EndOfFile;
+ return buf;
+}
-// SGI/MIPS specific.
-pub const CFA_MIPS_advance_loc8 = 0x1d;
+fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
+ const buf = try readAllocBytes(allocator, in_stream, size);
+ return FormValue{ .Block = buf };
+}
-// GNU extensions.
-pub const CFA_GNU_window_save = 0x2d;
-pub const CFA_GNU_args_size = 0x2e;
-pub const CFA_GNU_negative_offset_extended = 0x2f;
+// TODO the noasyncs here are workarounds
+fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
+ const block_len = try noasync in_stream.readVarInt(usize, builtin.Endian.Little, size);
+ return parseFormValueBlockLen(allocator, in_stream, block_len);
+}
-pub const CHILDREN_no = 0x00;
-pub const CHILDREN_yes = 0x01;
+fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, comptime size: i32) !FormValue {
+ // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here.
+ // `noasync` should be removed from all the function calls once it is fixed.
+ return FormValue{
+ .Const = Constant{
+ .signed = signed,
+ .payload = switch (size) {
+ 1 => try noasync in_stream.readIntLittle(u8),
+ 2 => try noasync in_stream.readIntLittle(u16),
+ 4 => try noasync in_stream.readIntLittle(u32),
+ 8 => try noasync in_stream.readIntLittle(u64),
+ -1 => blk: {
+ if (signed) {
+ const x = try noasync leb.readILEB128(i64, in_stream);
+ break :blk @bitCast(u64, x);
+ } else {
+ const x = try noasync leb.readULEB128(u64, in_stream);
+ break :blk x;
+ }
+ },
+ else => @compileError("Invalid size"),
+ },
+ },
+ };
+}
-pub const LNS_extended_op = 0x00;
-pub const LNS_copy = 0x01;
-pub const LNS_advance_pc = 0x02;
-pub const LNS_advance_line = 0x03;
-pub const LNS_set_file = 0x04;
-pub const LNS_set_column = 0x05;
-pub const LNS_negate_stmt = 0x06;
-pub const LNS_set_basic_block = 0x07;
-pub const LNS_const_add_pc = 0x08;
-pub const LNS_fixed_advance_pc = 0x09;
-pub const LNS_set_prologue_end = 0x0a;
-pub const LNS_set_epilogue_begin = 0x0b;
-pub const LNS_set_isa = 0x0c;
+// TODO the noasyncs here are workarounds
+fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 {
+ return if (is_64) try noasync in_stream.readIntLittle(u64) else @as(u64, try noasync in_stream.readIntLittle(u32));
+}
-pub const LNE_end_sequence = 0x01;
-pub const LNE_set_address = 0x02;
-pub const LNE_define_file = 0x03;
-pub const LNE_set_discriminator = 0x04;
-pub const LNE_lo_user = 0x80;
-pub const LNE_hi_user = 0xff;
+// TODO the noasyncs here are workarounds
+fn parseFormValueTargetAddrSize(in_stream: var) !u64 {
+ if (@sizeOf(usize) == 4) {
+ // TODO this cast should not be needed
+ return @as(u64, try noasync in_stream.readIntLittle(u32));
+ } else if (@sizeOf(usize) == 8) {
+ return noasync in_stream.readIntLittle(u64);
+ } else {
+ unreachable;
+ }
+}
-pub const LANG_C89 = 0x0001;
-pub const LANG_C = 0x0002;
-pub const LANG_Ada83 = 0x0003;
-pub const LANG_C_plus_plus = 0x0004;
-pub const LANG_Cobol74 = 0x0005;
-pub const LANG_Cobol85 = 0x0006;
-pub const LANG_Fortran77 = 0x0007;
-pub const LANG_Fortran90 = 0x0008;
-pub const LANG_Pascal83 = 0x0009;
-pub const LANG_Modula2 = 0x000a;
-pub const LANG_Java = 0x000b;
-pub const LANG_C99 = 0x000c;
-pub const LANG_Ada95 = 0x000d;
-pub const LANG_Fortran95 = 0x000e;
-pub const LANG_PLI = 0x000f;
-pub const LANG_ObjC = 0x0010;
-pub const LANG_ObjC_plus_plus = 0x0011;
-pub const LANG_UPC = 0x0012;
-pub const LANG_D = 0x0013;
-pub const LANG_Python = 0x0014;
-pub const LANG_Go = 0x0016;
-pub const LANG_C_plus_plus_11 = 0x001a;
-pub const LANG_Rust = 0x001c;
-pub const LANG_C11 = 0x001d;
-pub const LANG_C_plus_plus_14 = 0x0021;
-pub const LANG_Fortran03 = 0x0022;
-pub const LANG_Fortran08 = 0x0023;
-pub const LANG_lo_user = 0x8000;
-pub const LANG_hi_user = 0xffff;
-pub const LANG_Mips_Assembler = 0x8001;
-pub const LANG_Upc = 0x8765;
-pub const LANG_HP_Bliss = 0x8003;
-pub const LANG_HP_Basic91 = 0x8004;
-pub const LANG_HP_Pascal91 = 0x8005;
-pub const LANG_HP_IMacro = 0x8006;
-pub const LANG_HP_Assembler = 0x8007;
+// TODO the noasyncs here are workarounds
+fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, size: i32) !FormValue {
+ return FormValue{
+ .Ref = switch (size) {
+ 1 => try noasync in_stream.readIntLittle(u8),
+ 2 => try noasync in_stream.readIntLittle(u16),
+ 4 => try noasync in_stream.readIntLittle(u32),
+ 8 => try noasync in_stream.readIntLittle(u64),
+ -1 => try noasync leb.readULEB128(u64, in_stream),
+ else => unreachable,
+ },
+ };
+}
+
+// TODO the noasyncs here are workarounds
+fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue {
+ return switch (form_id) {
+ FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) },
+ FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),
+ FORM_block2 => parseFormValueBlock(allocator, in_stream, 2),
+ FORM_block4 => parseFormValueBlock(allocator, in_stream, 4),
+ FORM_block => x: {
+ const block_len = try noasync leb.readULEB128(usize, in_stream);
+ return parseFormValueBlockLen(allocator, in_stream, block_len);
+ },
+ FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1),
+ FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2),
+ FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4),
+ FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8),
+ FORM_udata, FORM_sdata => {
+ const signed = form_id == FORM_sdata;
+ return parseFormValueConstant(allocator, in_stream, signed, -1);
+ },
+ FORM_exprloc => {
+ const size = try noasync leb.readULEB128(usize, in_stream);
+ const buf = try readAllocBytes(allocator, in_stream, size);
+ return FormValue{ .ExprLoc = buf };
+ },
+ FORM_flag => FormValue{ .Flag = (try noasync in_stream.readByte()) != 0 },
+ FORM_flag_present => FormValue{ .Flag = true },
+ FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
+
+ FORM_ref1 => parseFormValueRef(allocator, in_stream, 1),
+ FORM_ref2 => parseFormValueRef(allocator, in_stream, 2),
+ FORM_ref4 => parseFormValueRef(allocator, in_stream, 4),
+ FORM_ref8 => parseFormValueRef(allocator, in_stream, 8),
+ FORM_ref_udata => parseFormValueRef(allocator, in_stream, -1),
+
+ FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
+ FORM_ref_sig8 => FormValue{ .Ref = try noasync in_stream.readIntLittle(u64) },
+
+ FORM_string => FormValue{ .String = try in_stream.readUntilDelimiterAlloc(allocator, 0, math.maxInt(usize)) },
+ FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
+ FORM_indirect => {
+ const child_form_id = try noasync leb.readULEB128(u64, in_stream);
+ const F = @TypeOf(async parseFormValue(allocator, in_stream, child_form_id, is_64));
+ var frame = try allocator.create(F);
+ defer allocator.destroy(frame);
+ return await @asyncCall(frame, {}, parseFormValue, allocator, in_stream, child_form_id, is_64);
+ },
+ else => error.InvalidDebugInfo,
+ };
+}
+
+fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry {
+ for (abbrev_table.toSliceConst()) |*table_entry| {
+ if (table_entry.abbrev_code == abbrev_code) return table_entry;
+ }
+ return null;
+}
+
+pub const DwarfInfo = struct {
+ endian: builtin.Endian,
+ // No memory is owned by the DwarfInfo
+ debug_info: []const u8,
+ debug_abbrev: []const u8,
+ debug_str: []const u8,
+ debug_line: []const u8,
+ debug_ranges: ?[]const u8,
+ // Filled later by the initializer
+ abbrev_table_list: ArrayList(AbbrevTableHeader) = undefined,
+ compile_unit_list: ArrayList(CompileUnit) = undefined,
+ func_list: ArrayList(Func) = undefined,
+
+ pub fn allocator(self: DwarfInfo) *mem.Allocator {
+ return self.abbrev_table_list.allocator;
+ }
+
+ fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 {
+ for (di.func_list.toSliceConst()) |*func| {
+ if (func.pc_range) |range| {
+ if (address >= range.start and address < range.end) {
+ return func.name;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ fn scanAllFunctions(di: *DwarfInfo) !void {
+ var s = io.SliceSeekableInStream.init(di.debug_info);
+ var this_unit_offset: u64 = 0;
+
+ while (this_unit_offset < try s.seekable_stream.getEndPos()) {
+ s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
+ error.EndOfStream => unreachable,
+ else => return err,
+ };
+
+ var is_64: bool = undefined;
+ const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
+ if (unit_length == 0) return;
+ const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
+
+ const version = try s.stream.readInt(u16, di.endian);
+ if (version < 2 or version > 5) return error.InvalidDebugInfo;
+
+ const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
+
+ const address_size = try s.stream.readByte();
+ if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
+
+ const compile_unit_pos = try s.seekable_stream.getPos();
+ const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
+
+ try s.seekable_stream.seekTo(compile_unit_pos);
+
+ const next_unit_pos = this_unit_offset + next_offset;
+
+ while ((try s.seekable_stream.getPos()) < next_unit_pos) {
+ const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue;
+ defer die_obj.attrs.deinit();
+
+ const after_die_offset = try s.seekable_stream.getPos();
+
+ switch (die_obj.tag_id) {
+ TAG_subprogram, TAG_inlined_subroutine, TAG_subroutine, TAG_entry_point => {
+ const fn_name = x: {
+ var depth: i32 = 3;
+ var this_die_obj = die_obj;
+ // Prenvent endless loops
+ while (depth > 0) : (depth -= 1) {
+ if (this_die_obj.getAttr(AT_name)) |_| {
+ const name = try this_die_obj.getAttrString(di, AT_name);
+ break :x name;
+ } else if (this_die_obj.getAttr(AT_abstract_origin)) |ref| {
+ // Follow the DIE it points to and repeat
+ const ref_offset = try this_die_obj.getAttrRef(AT_abstract_origin);
+ if (ref_offset > next_offset) return error.InvalidDebugInfo;
+ try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
+ this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
+ } else if (this_die_obj.getAttr(AT_specification)) |ref| {
+ // Follow the DIE it points to and repeat
+ const ref_offset = try this_die_obj.getAttrRef(AT_specification);
+ if (ref_offset > next_offset) return error.InvalidDebugInfo;
+ try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
+ this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
+ } else {
+ break :x null;
+ }
+ }
+
+ break :x null;
+ };
+
+ const pc_range = x: {
+ if (die_obj.getAttrAddr(AT_low_pc)) |low_pc| {
+ if (die_obj.getAttr(AT_high_pc)) |high_pc_value| {
+ const pc_end = switch (high_pc_value.*) {
+ FormValue.Address => |value| value,
+ FormValue.Const => |value| b: {
+ const offset = try value.asUnsignedLe();
+ break :b (low_pc + offset);
+ },
+ else => return error.InvalidDebugInfo,
+ };
+ break :x PcRange{
+ .start = low_pc,
+ .end = pc_end,
+ };
+ } else {
+ break :x null;
+ }
+ } else |err| {
+ if (err != error.MissingDebugInfo) return err;
+ break :x null;
+ }
+ };
+
+ try di.func_list.append(Func{
+ .name = fn_name,
+ .pc_range = pc_range,
+ });
+ },
+ else => {},
+ }
+
+ try s.seekable_stream.seekTo(after_die_offset);
+ }
+
+ this_unit_offset += next_offset;
+ }
+ }
+
+ fn scanAllCompileUnits(di: *DwarfInfo) !void {
+ var s = io.SliceSeekableInStream.init(di.debug_info);
+ var this_unit_offset: u64 = 0;
+
+ while (this_unit_offset < try s.seekable_stream.getEndPos()) {
+ s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
+ error.EndOfStream => unreachable,
+ else => return err,
+ };
+
+ var is_64: bool = undefined;
+ const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
+ if (unit_length == 0) return;
+ const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
+
+ const version = try s.stream.readInt(u16, di.endian);
+ if (version < 2 or version > 5) return error.InvalidDebugInfo;
+
+ const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
+
+ const address_size = try s.stream.readByte();
+ if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
+
+ const compile_unit_pos = try s.seekable_stream.getPos();
+ const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
+
+ try s.seekable_stream.seekTo(compile_unit_pos);
+
+ const compile_unit_die = try di.allocator().create(Die);
+ compile_unit_die.* = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
+
+ if (compile_unit_die.tag_id != TAG_compile_unit) return error.InvalidDebugInfo;
+
+ const pc_range = x: {
+ if (compile_unit_die.getAttrAddr(AT_low_pc)) |low_pc| {
+ if (compile_unit_die.getAttr(AT_high_pc)) |high_pc_value| {
+ const pc_end = switch (high_pc_value.*) {
+ FormValue.Address => |value| value,
+ FormValue.Const => |value| b: {
+ const offset = try value.asUnsignedLe();
+ break :b (low_pc + offset);
+ },
+ else => return error.InvalidDebugInfo,
+ };
+ break :x PcRange{
+ .start = low_pc,
+ .end = pc_end,
+ };
+ } else {
+ break :x null;
+ }
+ } else |err| {
+ if (err != error.MissingDebugInfo) return err;
+ break :x null;
+ }
+ };
+
+ try di.compile_unit_list.append(CompileUnit{
+ .version = version,
+ .is_64 = is_64,
+ .pc_range = pc_range,
+ .die = compile_unit_die,
+ });
+
+ this_unit_offset += next_offset;
+ }
+ }
+
+ fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {
+ for (di.compile_unit_list.toSlice()) |*compile_unit| {
+ if (compile_unit.pc_range) |range| {
+ if (target_address >= range.start and target_address < range.end) return compile_unit;
+ }
+ if (di.debug_ranges) |debug_ranges| {
+ if (compile_unit.die.getAttrSecOffset(AT_ranges)) |ranges_offset| {
+ var s = io.SliceSeekableInStream.init(debug_ranges);
+
+ // All the addresses in the list are relative to the value
+ // specified by DW_AT_low_pc or to some other value encoded
+ // in the list itself.
+ // If no starting value is specified use zero.
+ var base_address = compile_unit.die.getAttrAddr(AT_low_pc) catch |err| switch (err) {
+ error.MissingDebugInfo => 0,
+ else => return err,
+ };
+
+ try s.seekable_stream.seekTo(ranges_offset);
+
+ while (true) {
+ const begin_addr = try s.stream.readIntLittle(usize);
+ const end_addr = try s.stream.readIntLittle(usize);
+ if (begin_addr == 0 and end_addr == 0) {
+ break;
+ }
+ // This entry selects a new value for the base address
+ if (begin_addr == math.maxInt(usize)) {
+ base_address = end_addr;
+ continue;
+ }
+ if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) {
+ return compile_unit;
+ }
+ }
+ } else |err| {
+ if (err != error.MissingDebugInfo) return err;
+ continue;
+ }
+ }
+ }
+ return error.MissingDebugInfo;
+ }
+
+ /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
+ /// seeks in the stream and parses it.
+ fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable {
+ for (di.abbrev_table_list.toSlice()) |*header| {
+ if (header.offset == abbrev_offset) {
+ return &header.table;
+ }
+ }
+ try di.abbrev_table_list.append(AbbrevTableHeader{
+ .offset = abbrev_offset,
+ .table = try di.parseAbbrevTable(abbrev_offset),
+ });
+ return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table;
+ }
+
+ fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable {
+ var s = io.SliceSeekableInStream.init(di.debug_abbrev);
+
+ try s.seekable_stream.seekTo(offset);
+ var result = AbbrevTable.init(di.allocator());
+ errdefer result.deinit();
+ while (true) {
+ const abbrev_code = try leb.readULEB128(u64, &s.stream);
+ if (abbrev_code == 0) return result;
+ try result.append(AbbrevTableEntry{
+ .abbrev_code = abbrev_code,
+ .tag_id = try leb.readULEB128(u64, &s.stream),
+ .has_children = (try s.stream.readByte()) == CHILDREN_yes,
+ .attrs = ArrayList(AbbrevAttr).init(di.allocator()),
+ });
+ const attrs = &result.items[result.len - 1].attrs;
+
+ while (true) {
+ const attr_id = try leb.readULEB128(u64, &s.stream);
+ const form_id = try leb.readULEB128(u64, &s.stream);
+ if (attr_id == 0 and form_id == 0) break;
+ try attrs.append(AbbrevAttr{
+ .attr_id = attr_id,
+ .form_id = form_id,
+ });
+ }
+ }
+ }
+
+ fn parseDie(di: *DwarfInfo, in_stream: var, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {
+ const abbrev_code = try leb.readULEB128(u64, in_stream);
+ if (abbrev_code == 0) return null;
+ const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
+
+ var result = Die{
+ .tag_id = table_entry.tag_id,
+ .has_children = table_entry.has_children,
+ .attrs = ArrayList(Die.Attr).init(di.allocator()),
+ };
+ try result.attrs.resize(table_entry.attrs.len);
+ for (table_entry.attrs.toSliceConst()) |attr, i| {
+ result.attrs.items[i] = Die.Attr{
+ .id = attr.attr_id,
+ .value = try parseFormValue(di.allocator(), in_stream, attr.form_id, is_64),
+ };
+ }
+ return result;
+ }
+
+ fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !debug.LineInfo {
+ var s = io.SliceSeekableInStream.init(di.debug_line);
+
+ const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT_comp_dir);
+ const line_info_offset = try compile_unit.die.getAttrSecOffset(AT_stmt_list);
+
+ try s.seekable_stream.seekTo(line_info_offset);
+
+ var is_64: bool = undefined;
+ const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
+ if (unit_length == 0) {
+ return error.MissingDebugInfo;
+ }
+ const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
+
+ const version = try s.stream.readInt(u16, di.endian);
+ // TODO support 3 and 5
+ if (version != 2 and version != 4) return error.InvalidDebugInfo;
+
+ const prologue_length = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
+ const prog_start_offset = (try s.seekable_stream.getPos()) + prologue_length;
+
+ const minimum_instruction_length = try s.stream.readByte();
+ if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
+
+ if (version >= 4) {
+ // maximum_operations_per_instruction
+ _ = try s.stream.readByte();
+ }
+
+ const default_is_stmt = (try s.stream.readByte()) != 0;
+ const line_base = try s.stream.readByteSigned();
+
+ const line_range = try s.stream.readByte();
+ if (line_range == 0) return error.InvalidDebugInfo;
+
+ const opcode_base = try s.stream.readByte();
+
+ const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
+ defer di.allocator().free(standard_opcode_lengths);
+
+ {
+ var i: usize = 0;
+ while (i < opcode_base - 1) : (i += 1) {
+ standard_opcode_lengths[i] = try s.stream.readByte();
+ }
+ }
+
+ var include_directories = ArrayList([]const u8).init(di.allocator());
+ try include_directories.append(compile_unit_cwd);
+ while (true) {
+ const dir = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
+ if (dir.len == 0) break;
+ try include_directories.append(dir);
+ }
+
+ var file_entries = ArrayList(FileEntry).init(di.allocator());
+ var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
+
+ while (true) {
+ const file_name = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
+ if (file_name.len == 0) break;
+ const dir_index = try leb.readULEB128(usize, &s.stream);
+ const mtime = try leb.readULEB128(usize, &s.stream);
+ const len_bytes = try leb.readULEB128(usize, &s.stream);
+ try file_entries.append(FileEntry{
+ .file_name = file_name,
+ .dir_index = dir_index,
+ .mtime = mtime,
+ .len_bytes = len_bytes,
+ });
+ }
+
+ try s.seekable_stream.seekTo(prog_start_offset);
+
+ const next_unit_pos = line_info_offset + next_offset;
+
+ while ((try s.seekable_stream.getPos()) < next_unit_pos) {
+ const opcode = try s.stream.readByte();
+
+ if (opcode == LNS_extended_op) {
+ const op_size = try leb.readULEB128(u64, &s.stream);
+ if (op_size < 1) return error.InvalidDebugInfo;
+ var sub_op = try s.stream.readByte();
+ switch (sub_op) {
+ LNE_end_sequence => {
+ prog.end_sequence = true;
+ if (try prog.checkLineMatch()) |info| return info;
+ prog.reset();
+ },
+ LNE_set_address => {
+ const addr = try s.stream.readInt(usize, di.endian);
+ prog.address = addr;
+ },
+ LNE_define_file => {
+ const file_name = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
+ const dir_index = try leb.readULEB128(usize, &s.stream);
+ const mtime = try leb.readULEB128(usize, &s.stream);
+ const len_bytes = try leb.readULEB128(usize, &s.stream);
+ try file_entries.append(FileEntry{
+ .file_name = file_name,
+ .dir_index = dir_index,
+ .mtime = mtime,
+ .len_bytes = len_bytes,
+ });
+ },
+ else => {
+ const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
+ try s.seekable_stream.seekBy(fwd_amt);
+ },
+ }
+ } else if (opcode >= opcode_base) {
+ // special opcodes
+ const adjusted_opcode = opcode - opcode_base;
+ const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
+ const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range);
+ prog.line += inc_line;
+ prog.address += inc_addr;
+ if (try prog.checkLineMatch()) |info| return info;
+ prog.basic_block = false;
+ } else {
+ switch (opcode) {
+ LNS_copy => {
+ if (try prog.checkLineMatch()) |info| return info;
+ prog.basic_block = false;
+ },
+ LNS_advance_pc => {
+ const arg = try leb.readULEB128(usize, &s.stream);
+ prog.address += arg * minimum_instruction_length;
+ },
+ LNS_advance_line => {
+ const arg = try leb.readILEB128(i64, &s.stream);
+ prog.line += arg;
+ },
+ LNS_set_file => {
+ const arg = try leb.readULEB128(usize, &s.stream);
+ prog.file = arg;
+ },
+ LNS_set_column => {
+ const arg = try leb.readULEB128(u64, &s.stream);
+ prog.column = arg;
+ },
+ LNS_negate_stmt => {
+ prog.is_stmt = !prog.is_stmt;
+ },
+ LNS_set_basic_block => {
+ prog.basic_block = true;
+ },
+ LNS_const_add_pc => {
+ const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
+ prog.address += inc_addr;
+ },
+ LNS_fixed_advance_pc => {
+ const arg = try s.stream.readInt(u16, di.endian);
+ prog.address += arg;
+ },
+ LNS_set_prologue_end => {},
+ else => {
+ if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
+ const len_bytes = standard_opcode_lengths[opcode - 1];
+ try s.seekable_stream.seekBy(len_bytes);
+ },
+ }
+ }
+ }
+
+ return error.MissingDebugInfo;
+ }
+
+ fn getString(di: *DwarfInfo, offset: u64) ![]const u8 {
+ if (offset > di.debug_str.len)
+ return error.InvalidDebugInfo;
+ const casted_offset = math.cast(usize, offset) catch
+ return error.InvalidDebugInfo;
+
+ // Valid strings always have a terminating zero byte
+ if (mem.indexOfScalarPos(u8, di.debug_str, casted_offset, 0)) |last| {
+ return di.debug_str[casted_offset..last];
+ }
+
+ return error.InvalidDebugInfo;
+ }
+};
+
+/// Initialize DWARF info. The caller has the responsibility to initialize most
+/// the DwarfInfo fields before calling. These fields can be left undefined:
+/// * abbrev_table_list
+/// * compile_unit_list
+pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void {
+ di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator);
+ di.compile_unit_list = ArrayList(CompileUnit).init(allocator);
+ di.func_list = ArrayList(Func).init(allocator);
+ try di.scanAllFunctions();
+ try di.scanAllCompileUnits();
+}
diff --git a/lib/std/dwarf_bits.zig b/lib/std/dwarf_bits.zig
new file mode 100644
index 0000000000..2f3b29302d
--- /dev/null
+++ b/lib/std/dwarf_bits.zig
@@ -0,0 +1,682 @@
+pub const TAG_padding = 0x00;
+pub const TAG_array_type = 0x01;
+pub const TAG_class_type = 0x02;
+pub const TAG_entry_point = 0x03;
+pub const TAG_enumeration_type = 0x04;
+pub const TAG_formal_parameter = 0x05;
+pub const TAG_imported_declaration = 0x08;
+pub const TAG_label = 0x0a;
+pub const TAG_lexical_block = 0x0b;
+pub const TAG_member = 0x0d;
+pub const TAG_pointer_type = 0x0f;
+pub const TAG_reference_type = 0x10;
+pub const TAG_compile_unit = 0x11;
+pub const TAG_string_type = 0x12;
+pub const TAG_structure_type = 0x13;
+pub const TAG_subroutine = 0x14;
+pub const TAG_subroutine_type = 0x15;
+pub const TAG_typedef = 0x16;
+pub const TAG_union_type = 0x17;
+pub const TAG_unspecified_parameters = 0x18;
+pub const TAG_variant = 0x19;
+pub const TAG_common_block = 0x1a;
+pub const TAG_common_inclusion = 0x1b;
+pub const TAG_inheritance = 0x1c;
+pub const TAG_inlined_subroutine = 0x1d;
+pub const TAG_module = 0x1e;
+pub const TAG_ptr_to_member_type = 0x1f;
+pub const TAG_set_type = 0x20;
+pub const TAG_subrange_type = 0x21;
+pub const TAG_with_stmt = 0x22;
+pub const TAG_access_declaration = 0x23;
+pub const TAG_base_type = 0x24;
+pub const TAG_catch_block = 0x25;
+pub const TAG_const_type = 0x26;
+pub const TAG_constant = 0x27;
+pub const TAG_enumerator = 0x28;
+pub const TAG_file_type = 0x29;
+pub const TAG_friend = 0x2a;
+pub const TAG_namelist = 0x2b;
+pub const TAG_namelist_item = 0x2c;
+pub const TAG_packed_type = 0x2d;
+pub const TAG_subprogram = 0x2e;
+pub const TAG_template_type_param = 0x2f;
+pub const TAG_template_value_param = 0x30;
+pub const TAG_thrown_type = 0x31;
+pub const TAG_try_block = 0x32;
+pub const TAG_variant_part = 0x33;
+pub const TAG_variable = 0x34;
+pub const TAG_volatile_type = 0x35;
+
+// DWARF 3
+pub const TAG_dwarf_procedure = 0x36;
+pub const TAG_restrict_type = 0x37;
+pub const TAG_interface_type = 0x38;
+pub const TAG_namespace = 0x39;
+pub const TAG_imported_module = 0x3a;
+pub const TAG_unspecified_type = 0x3b;
+pub const TAG_partial_unit = 0x3c;
+pub const TAG_imported_unit = 0x3d;
+pub const TAG_condition = 0x3f;
+pub const TAG_shared_type = 0x40;
+
+// DWARF 4
+pub const TAG_type_unit = 0x41;
+pub const TAG_rvalue_reference_type = 0x42;
+pub const TAG_template_alias = 0x43;
+
+pub const TAG_lo_user = 0x4080;
+pub const TAG_hi_user = 0xffff;
+
+// SGI/MIPS Extensions.
+pub const DW_TAG_MIPS_loop = 0x4081;
+
+// HP extensions. See: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz .
+pub const TAG_HP_array_descriptor = 0x4090;
+pub const TAG_HP_Bliss_field = 0x4091;
+pub const TAG_HP_Bliss_field_set = 0x4092;
+
+// GNU extensions.
+pub const TAG_format_label = 0x4101; // For FORTRAN 77 and Fortran 90.
+pub const TAG_function_template = 0x4102; // For C++.
+pub const TAG_class_template = 0x4103; //For C++.
+pub const TAG_GNU_BINCL = 0x4104;
+pub const TAG_GNU_EINCL = 0x4105;
+
+// Template template parameter.
+// See http://gcc.gnu.org/wiki/TemplateParmsDwarf .
+pub const TAG_GNU_template_template_param = 0x4106;
+
+// Template parameter pack extension = specified at
+// http://wiki.dwarfstd.org/index.php?title=C%2B%2B0x:_Variadic_templates
+// The values of these two TAGS are in the DW_TAG_GNU_* space until the tags
+// are properly part of DWARF 5.
+pub const TAG_GNU_template_parameter_pack = 0x4107;
+pub const TAG_GNU_formal_parameter_pack = 0x4108;
+// The GNU call site extension = specified at
+// http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open .
+// The values of these two TAGS are in the DW_TAG_GNU_* space until the tags
+// are properly part of DWARF 5.
+pub const TAG_GNU_call_site = 0x4109;
+pub const TAG_GNU_call_site_parameter = 0x410a;
+// Extensions for UPC. See: http://dwarfstd.org/doc/DWARF4.pdf.
+pub const TAG_upc_shared_type = 0x8765;
+pub const TAG_upc_strict_type = 0x8766;
+pub const TAG_upc_relaxed_type = 0x8767;
+// PGI (STMicroelectronics; extensions. No documentation available.
+pub const TAG_PGI_kanji_type = 0xA000;
+pub const TAG_PGI_interface_block = 0xA020;
+
+pub const FORM_addr = 0x01;
+pub const FORM_block2 = 0x03;
+pub const FORM_block4 = 0x04;
+pub const FORM_data2 = 0x05;
+pub const FORM_data4 = 0x06;
+pub const FORM_data8 = 0x07;
+pub const FORM_string = 0x08;
+pub const FORM_block = 0x09;
+pub const FORM_block1 = 0x0a;
+pub const FORM_data1 = 0x0b;
+pub const FORM_flag = 0x0c;
+pub const FORM_sdata = 0x0d;
+pub const FORM_strp = 0x0e;
+pub const FORM_udata = 0x0f;
+pub const FORM_ref_addr = 0x10;
+pub const FORM_ref1 = 0x11;
+pub const FORM_ref2 = 0x12;
+pub const FORM_ref4 = 0x13;
+pub const FORM_ref8 = 0x14;
+pub const FORM_ref_udata = 0x15;
+pub const FORM_indirect = 0x16;
+pub const FORM_sec_offset = 0x17;
+pub const FORM_exprloc = 0x18;
+pub const FORM_flag_present = 0x19;
+pub const FORM_ref_sig8 = 0x20;
+
+// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
+pub const FORM_GNU_addr_index = 0x1f01;
+pub const FORM_GNU_str_index = 0x1f02;
+
+// Extensions for DWZ multifile.
+// See http://www.dwarfstd.org/ShowIssue.php?issue=120604.1&type=open .
+pub const FORM_GNU_ref_alt = 0x1f20;
+pub const FORM_GNU_strp_alt = 0x1f21;
+
+pub const AT_sibling = 0x01;
+pub const AT_location = 0x02;
+pub const AT_name = 0x03;
+pub const AT_ordering = 0x09;
+pub const AT_subscr_data = 0x0a;
+pub const AT_byte_size = 0x0b;
+pub const AT_bit_offset = 0x0c;
+pub const AT_bit_size = 0x0d;
+pub const AT_element_list = 0x0f;
+pub const AT_stmt_list = 0x10;
+pub const AT_low_pc = 0x11;
+pub const AT_high_pc = 0x12;
+pub const AT_language = 0x13;
+pub const AT_member = 0x14;
+pub const AT_discr = 0x15;
+pub const AT_discr_value = 0x16;
+pub const AT_visibility = 0x17;
+pub const AT_import = 0x18;
+pub const AT_string_length = 0x19;
+pub const AT_common_reference = 0x1a;
+pub const AT_comp_dir = 0x1b;
+pub const AT_const_value = 0x1c;
+pub const AT_containing_type = 0x1d;
+pub const AT_default_value = 0x1e;
+pub const AT_inline = 0x20;
+pub const AT_is_optional = 0x21;
+pub const AT_lower_bound = 0x22;
+pub const AT_producer = 0x25;
+pub const AT_prototyped = 0x27;
+pub const AT_return_addr = 0x2a;
+pub const AT_start_scope = 0x2c;
+pub const AT_bit_stride = 0x2e;
+pub const AT_upper_bound = 0x2f;
+pub const AT_abstract_origin = 0x31;
+pub const AT_accessibility = 0x32;
+pub const AT_address_class = 0x33;
+pub const AT_artificial = 0x34;
+pub const AT_base_types = 0x35;
+pub const AT_calling_convention = 0x36;
+pub const AT_count = 0x37;
+pub const AT_data_member_location = 0x38;
+pub const AT_decl_column = 0x39;
+pub const AT_decl_file = 0x3a;
+pub const AT_decl_line = 0x3b;
+pub const AT_declaration = 0x3c;
+pub const AT_discr_list = 0x3d;
+pub const AT_encoding = 0x3e;
+pub const AT_external = 0x3f;
+pub const AT_frame_base = 0x40;
+pub const AT_friend = 0x41;
+pub const AT_identifier_case = 0x42;
+pub const AT_macro_info = 0x43;
+pub const AT_namelist_items = 0x44;
+pub const AT_priority = 0x45;
+pub const AT_segment = 0x46;
+pub const AT_specification = 0x47;
+pub const AT_static_link = 0x48;
+pub const AT_type = 0x49;
+pub const AT_use_location = 0x4a;
+pub const AT_variable_parameter = 0x4b;
+pub const AT_virtuality = 0x4c;
+pub const AT_vtable_elem_location = 0x4d;
+
+// DWARF 3 values.
+pub const AT_allocated = 0x4e;
+pub const AT_associated = 0x4f;
+pub const AT_data_location = 0x50;
+pub const AT_byte_stride = 0x51;
+pub const AT_entry_pc = 0x52;
+pub const AT_use_UTF8 = 0x53;
+pub const AT_extension = 0x54;
+pub const AT_ranges = 0x55;
+pub const AT_trampoline = 0x56;
+pub const AT_call_column = 0x57;
+pub const AT_call_file = 0x58;
+pub const AT_call_line = 0x59;
+pub const AT_description = 0x5a;
+pub const AT_binary_scale = 0x5b;
+pub const AT_decimal_scale = 0x5c;
+pub const AT_small = 0x5d;
+pub const AT_decimal_sign = 0x5e;
+pub const AT_digit_count = 0x5f;
+pub const AT_picture_string = 0x60;
+pub const AT_mutable = 0x61;
+pub const AT_threads_scaled = 0x62;
+pub const AT_explicit = 0x63;
+pub const AT_object_pointer = 0x64;
+pub const AT_endianity = 0x65;
+pub const AT_elemental = 0x66;
+pub const AT_pure = 0x67;
+pub const AT_recursive = 0x68;
+
+// DWARF 4.
+pub const AT_signature = 0x69;
+pub const AT_main_subprogram = 0x6a;
+pub const AT_data_bit_offset = 0x6b;
+pub const AT_const_expr = 0x6c;
+pub const AT_enum_class = 0x6d;
+pub const AT_linkage_name = 0x6e;
+
+// DWARF 5
+pub const AT_alignment = 0x88;
+
+pub const AT_lo_user = 0x2000; // Implementation-defined range start.
+pub const AT_hi_user = 0x3fff; // Implementation-defined range end.
+
+// SGI/MIPS extensions.
+pub const AT_MIPS_fde = 0x2001;
+pub const AT_MIPS_loop_begin = 0x2002;
+pub const AT_MIPS_tail_loop_begin = 0x2003;
+pub const AT_MIPS_epilog_begin = 0x2004;
+pub const AT_MIPS_loop_unroll_factor = 0x2005;
+pub const AT_MIPS_software_pipeline_depth = 0x2006;
+pub const AT_MIPS_linkage_name = 0x2007;
+pub const AT_MIPS_stride = 0x2008;
+pub const AT_MIPS_abstract_name = 0x2009;
+pub const AT_MIPS_clone_origin = 0x200a;
+pub const AT_MIPS_has_inlines = 0x200b;
+
+// HP extensions.
+pub const AT_HP_block_index = 0x2000;
+pub const AT_HP_unmodifiable = 0x2001; // Same as DW_AT_MIPS_fde.
+pub const AT_HP_prologue = 0x2005; // Same as DW_AT_MIPS_loop_unroll.
+pub const AT_HP_epilogue = 0x2008; // Same as DW_AT_MIPS_stride.
+pub const AT_HP_actuals_stmt_list = 0x2010;
+pub const AT_HP_proc_per_section = 0x2011;
+pub const AT_HP_raw_data_ptr = 0x2012;
+pub const AT_HP_pass_by_reference = 0x2013;
+pub const AT_HP_opt_level = 0x2014;
+pub const AT_HP_prof_version_id = 0x2015;
+pub const AT_HP_opt_flags = 0x2016;
+pub const AT_HP_cold_region_low_pc = 0x2017;
+pub const AT_HP_cold_region_high_pc = 0x2018;
+pub const AT_HP_all_variables_modifiable = 0x2019;
+pub const AT_HP_linkage_name = 0x201a;
+pub const AT_HP_prof_flags = 0x201b; // In comp unit of procs_info for -g.
+pub const AT_HP_unit_name = 0x201f;
+pub const AT_HP_unit_size = 0x2020;
+pub const AT_HP_widened_byte_size = 0x2021;
+pub const AT_HP_definition_points = 0x2022;
+pub const AT_HP_default_location = 0x2023;
+pub const AT_HP_is_result_param = 0x2029;
+
+// GNU extensions.
+pub const AT_sf_names = 0x2101;
+pub const AT_src_info = 0x2102;
+pub const AT_mac_info = 0x2103;
+pub const AT_src_coords = 0x2104;
+pub const AT_body_begin = 0x2105;
+pub const AT_body_end = 0x2106;
+pub const AT_GNU_vector = 0x2107;
+// Thread-safety annotations.
+// See http://gcc.gnu.org/wiki/ThreadSafetyAnnotation .
+pub const AT_GNU_guarded_by = 0x2108;
+pub const AT_GNU_pt_guarded_by = 0x2109;
+pub const AT_GNU_guarded = 0x210a;
+pub const AT_GNU_pt_guarded = 0x210b;
+pub const AT_GNU_locks_excluded = 0x210c;
+pub const AT_GNU_exclusive_locks_required = 0x210d;
+pub const AT_GNU_shared_locks_required = 0x210e;
+// One-definition rule violation detection.
+// See http://gcc.gnu.org/wiki/DwarfSeparateTypeInfo .
+pub const AT_GNU_odr_signature = 0x210f;
+// Template template argument name.
+// See http://gcc.gnu.org/wiki/TemplateParmsDwarf .
+pub const AT_GNU_template_name = 0x2110;
+// The GNU call site extension.
+// See http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open .
+pub const AT_GNU_call_site_value = 0x2111;
+pub const AT_GNU_call_site_data_value = 0x2112;
+pub const AT_GNU_call_site_target = 0x2113;
+pub const AT_GNU_call_site_target_clobbered = 0x2114;
+pub const AT_GNU_tail_call = 0x2115;
+pub const AT_GNU_all_tail_call_sites = 0x2116;
+pub const AT_GNU_all_call_sites = 0x2117;
+pub const AT_GNU_all_source_call_sites = 0x2118;
+// Section offset into .debug_macro section.
+pub const AT_GNU_macros = 0x2119;
+// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
+pub const AT_GNU_dwo_name = 0x2130;
+pub const AT_GNU_dwo_id = 0x2131;
+pub const AT_GNU_ranges_base = 0x2132;
+pub const AT_GNU_addr_base = 0x2133;
+pub const AT_GNU_pubnames = 0x2134;
+pub const AT_GNU_pubtypes = 0x2135;
+// VMS extensions.
+pub const AT_VMS_rtnbeg_pd_address = 0x2201;
+// GNAT extensions.
+// GNAT descriptive type.
+// See http://gcc.gnu.org/wiki/DW_AT_GNAT_descriptive_type .
+pub const AT_use_GNAT_descriptive_type = 0x2301;
+pub const AT_GNAT_descriptive_type = 0x2302;
+// UPC extension.
+pub const AT_upc_threads_scaled = 0x3210;
+// PGI (STMicroelectronics) extensions.
+pub const AT_PGI_lbase = 0x3a00;
+pub const AT_PGI_soffset = 0x3a01;
+pub const AT_PGI_lstride = 0x3a02;
+
+pub const OP_addr = 0x03;
+pub const OP_deref = 0x06;
+pub const OP_const1u = 0x08;
+pub const OP_const1s = 0x09;
+pub const OP_const2u = 0x0a;
+pub const OP_const2s = 0x0b;
+pub const OP_const4u = 0x0c;
+pub const OP_const4s = 0x0d;
+pub const OP_const8u = 0x0e;
+pub const OP_const8s = 0x0f;
+pub const OP_constu = 0x10;
+pub const OP_consts = 0x11;
+pub const OP_dup = 0x12;
+pub const OP_drop = 0x13;
+pub const OP_over = 0x14;
+pub const OP_pick = 0x15;
+pub const OP_swap = 0x16;
+pub const OP_rot = 0x17;
+pub const OP_xderef = 0x18;
+pub const OP_abs = 0x19;
+pub const OP_and = 0x1a;
+pub const OP_div = 0x1b;
+pub const OP_minus = 0x1c;
+pub const OP_mod = 0x1d;
+pub const OP_mul = 0x1e;
+pub const OP_neg = 0x1f;
+pub const OP_not = 0x20;
+pub const OP_or = 0x21;
+pub const OP_plus = 0x22;
+pub const OP_plus_uconst = 0x23;
+pub const OP_shl = 0x24;
+pub const OP_shr = 0x25;
+pub const OP_shra = 0x26;
+pub const OP_xor = 0x27;
+pub const OP_bra = 0x28;
+pub const OP_eq = 0x29;
+pub const OP_ge = 0x2a;
+pub const OP_gt = 0x2b;
+pub const OP_le = 0x2c;
+pub const OP_lt = 0x2d;
+pub const OP_ne = 0x2e;
+pub const OP_skip = 0x2f;
+pub const OP_lit0 = 0x30;
+pub const OP_lit1 = 0x31;
+pub const OP_lit2 = 0x32;
+pub const OP_lit3 = 0x33;
+pub const OP_lit4 = 0x34;
+pub const OP_lit5 = 0x35;
+pub const OP_lit6 = 0x36;
+pub const OP_lit7 = 0x37;
+pub const OP_lit8 = 0x38;
+pub const OP_lit9 = 0x39;
+pub const OP_lit10 = 0x3a;
+pub const OP_lit11 = 0x3b;
+pub const OP_lit12 = 0x3c;
+pub const OP_lit13 = 0x3d;
+pub const OP_lit14 = 0x3e;
+pub const OP_lit15 = 0x3f;
+pub const OP_lit16 = 0x40;
+pub const OP_lit17 = 0x41;
+pub const OP_lit18 = 0x42;
+pub const OP_lit19 = 0x43;
+pub const OP_lit20 = 0x44;
+pub const OP_lit21 = 0x45;
+pub const OP_lit22 = 0x46;
+pub const OP_lit23 = 0x47;
+pub const OP_lit24 = 0x48;
+pub const OP_lit25 = 0x49;
+pub const OP_lit26 = 0x4a;
+pub const OP_lit27 = 0x4b;
+pub const OP_lit28 = 0x4c;
+pub const OP_lit29 = 0x4d;
+pub const OP_lit30 = 0x4e;
+pub const OP_lit31 = 0x4f;
+pub const OP_reg0 = 0x50;
+pub const OP_reg1 = 0x51;
+pub const OP_reg2 = 0x52;
+pub const OP_reg3 = 0x53;
+pub const OP_reg4 = 0x54;
+pub const OP_reg5 = 0x55;
+pub const OP_reg6 = 0x56;
+pub const OP_reg7 = 0x57;
+pub const OP_reg8 = 0x58;
+pub const OP_reg9 = 0x59;
+pub const OP_reg10 = 0x5a;
+pub const OP_reg11 = 0x5b;
+pub const OP_reg12 = 0x5c;
+pub const OP_reg13 = 0x5d;
+pub const OP_reg14 = 0x5e;
+pub const OP_reg15 = 0x5f;
+pub const OP_reg16 = 0x60;
+pub const OP_reg17 = 0x61;
+pub const OP_reg18 = 0x62;
+pub const OP_reg19 = 0x63;
+pub const OP_reg20 = 0x64;
+pub const OP_reg21 = 0x65;
+pub const OP_reg22 = 0x66;
+pub const OP_reg23 = 0x67;
+pub const OP_reg24 = 0x68;
+pub const OP_reg25 = 0x69;
+pub const OP_reg26 = 0x6a;
+pub const OP_reg27 = 0x6b;
+pub const OP_reg28 = 0x6c;
+pub const OP_reg29 = 0x6d;
+pub const OP_reg30 = 0x6e;
+pub const OP_reg31 = 0x6f;
+pub const OP_breg0 = 0x70;
+pub const OP_breg1 = 0x71;
+pub const OP_breg2 = 0x72;
+pub const OP_breg3 = 0x73;
+pub const OP_breg4 = 0x74;
+pub const OP_breg5 = 0x75;
+pub const OP_breg6 = 0x76;
+pub const OP_breg7 = 0x77;
+pub const OP_breg8 = 0x78;
+pub const OP_breg9 = 0x79;
+pub const OP_breg10 = 0x7a;
+pub const OP_breg11 = 0x7b;
+pub const OP_breg12 = 0x7c;
+pub const OP_breg13 = 0x7d;
+pub const OP_breg14 = 0x7e;
+pub const OP_breg15 = 0x7f;
+pub const OP_breg16 = 0x80;
+pub const OP_breg17 = 0x81;
+pub const OP_breg18 = 0x82;
+pub const OP_breg19 = 0x83;
+pub const OP_breg20 = 0x84;
+pub const OP_breg21 = 0x85;
+pub const OP_breg22 = 0x86;
+pub const OP_breg23 = 0x87;
+pub const OP_breg24 = 0x88;
+pub const OP_breg25 = 0x89;
+pub const OP_breg26 = 0x8a;
+pub const OP_breg27 = 0x8b;
+pub const OP_breg28 = 0x8c;
+pub const OP_breg29 = 0x8d;
+pub const OP_breg30 = 0x8e;
+pub const OP_breg31 = 0x8f;
+pub const OP_regx = 0x90;
+pub const OP_fbreg = 0x91;
+pub const OP_bregx = 0x92;
+pub const OP_piece = 0x93;
+pub const OP_deref_size = 0x94;
+pub const OP_xderef_size = 0x95;
+pub const OP_nop = 0x96;
+
+// DWARF 3 extensions.
+pub const OP_push_object_address = 0x97;
+pub const OP_call2 = 0x98;
+pub const OP_call4 = 0x99;
+pub const OP_call_ref = 0x9a;
+pub const OP_form_tls_address = 0x9b;
+pub const OP_call_frame_cfa = 0x9c;
+pub const OP_bit_piece = 0x9d;
+
+// DWARF 4 extensions.
+pub const OP_implicit_value = 0x9e;
+pub const OP_stack_value = 0x9f;
+
+pub const OP_lo_user = 0xe0; // Implementation-defined range start.
+pub const OP_hi_user = 0xff; // Implementation-defined range end.
+
+// GNU extensions.
+pub const OP_GNU_push_tls_address = 0xe0;
+// The following is for marking variables that are uninitialized.
+pub const OP_GNU_uninit = 0xf0;
+pub const OP_GNU_encoded_addr = 0xf1;
+// The GNU implicit pointer extension.
+// See http://www.dwarfstd.org/ShowIssue.php?issue=100831.1&type=open .
+pub const OP_GNU_implicit_pointer = 0xf2;
+// The GNU entry value extension.
+// See http://www.dwarfstd.org/ShowIssue.php?issue=100909.1&type=open .
+pub const OP_GNU_entry_value = 0xf3;
+// The GNU typed stack extension.
+// See http://www.dwarfstd.org/doc/040408.1.html .
+pub const OP_GNU_const_type = 0xf4;
+pub const OP_GNU_regval_type = 0xf5;
+pub const OP_GNU_deref_type = 0xf6;
+pub const OP_GNU_convert = 0xf7;
+pub const OP_GNU_reinterpret = 0xf9;
+// The GNU parameter ref extension.
+pub const OP_GNU_parameter_ref = 0xfa;
+// Extension for Fission. See http://gcc.gnu.org/wiki/DebugFission.
+pub const OP_GNU_addr_index = 0xfb;
+pub const OP_GNU_const_index = 0xfc;
+// HP extensions.
+pub const OP_HP_unknown = 0xe0; // Ouch, the same as GNU_push_tls_address.
+pub const OP_HP_is_value = 0xe1;
+pub const OP_HP_fltconst4 = 0xe2;
+pub const OP_HP_fltconst8 = 0xe3;
+pub const OP_HP_mod_range = 0xe4;
+pub const OP_HP_unmod_range = 0xe5;
+pub const OP_HP_tls = 0xe6;
+// PGI (STMicroelectronics) extensions.
+pub const OP_PGI_omp_thread_num = 0xf8;
+
+pub const ATE_void = 0x0;
+pub const ATE_address = 0x1;
+pub const ATE_boolean = 0x2;
+pub const ATE_complex_float = 0x3;
+pub const ATE_float = 0x4;
+pub const ATE_signed = 0x5;
+pub const ATE_signed_char = 0x6;
+pub const ATE_unsigned = 0x7;
+pub const ATE_unsigned_char = 0x8;
+
+// DWARF 3.
+pub const ATE_imaginary_float = 0x9;
+pub const ATE_packed_decimal = 0xa;
+pub const ATE_numeric_string = 0xb;
+pub const ATE_edited = 0xc;
+pub const ATE_signed_fixed = 0xd;
+pub const ATE_unsigned_fixed = 0xe;
+pub const ATE_decimal_float = 0xf;
+
+// DWARF 4.
+pub const ATE_UTF = 0x10;
+
+pub const ATE_lo_user = 0x80;
+pub const ATE_hi_user = 0xff;
+
+// HP extensions.
+pub const ATE_HP_float80 = 0x80; // Floating-point (80 bit).
+pub const ATE_HP_complex_float80 = 0x81; // Complex floating-point (80 bit).
+pub const ATE_HP_float128 = 0x82; // Floating-point (128 bit).
+pub const ATE_HP_complex_float128 = 0x83; // Complex fp (128 bit).
+pub const ATE_HP_floathpintel = 0x84; // Floating-point (82 bit IA64).
+pub const ATE_HP_imaginary_float80 = 0x85;
+pub const ATE_HP_imaginary_float128 = 0x86;
+pub const ATE_HP_VAX_float = 0x88; // F or G floating.
+pub const ATE_HP_VAX_float_d = 0x89; // D floating.
+pub const ATE_HP_packed_decimal = 0x8a; // Cobol.
+pub const ATE_HP_zoned_decimal = 0x8b; // Cobol.
+pub const ATE_HP_edited = 0x8c; // Cobol.
+pub const ATE_HP_signed_fixed = 0x8d; // Cobol.
+pub const ATE_HP_unsigned_fixed = 0x8e; // Cobol.
+pub const ATE_HP_VAX_complex_float = 0x8f; // F or G floating complex.
+pub const ATE_HP_VAX_complex_float_d = 0x90; // D floating complex.
+
+pub const CFA_advance_loc = 0x40;
+pub const CFA_offset = 0x80;
+pub const CFA_restore = 0xc0;
+pub const CFA_nop = 0x00;
+pub const CFA_set_loc = 0x01;
+pub const CFA_advance_loc1 = 0x02;
+pub const CFA_advance_loc2 = 0x03;
+pub const CFA_advance_loc4 = 0x04;
+pub const CFA_offset_extended = 0x05;
+pub const CFA_restore_extended = 0x06;
+pub const CFA_undefined = 0x07;
+pub const CFA_same_value = 0x08;
+pub const CFA_register = 0x09;
+pub const CFA_remember_state = 0x0a;
+pub const CFA_restore_state = 0x0b;
+pub const CFA_def_cfa = 0x0c;
+pub const CFA_def_cfa_register = 0x0d;
+pub const CFA_def_cfa_offset = 0x0e;
+
+// DWARF 3.
+pub const CFA_def_cfa_expression = 0x0f;
+pub const CFA_expression = 0x10;
+pub const CFA_offset_extended_sf = 0x11;
+pub const CFA_def_cfa_sf = 0x12;
+pub const CFA_def_cfa_offset_sf = 0x13;
+pub const CFA_val_offset = 0x14;
+pub const CFA_val_offset_sf = 0x15;
+pub const CFA_val_expression = 0x16;
+
+pub const CFA_lo_user = 0x1c;
+pub const CFA_hi_user = 0x3f;
+
+// SGI/MIPS specific.
+pub const CFA_MIPS_advance_loc8 = 0x1d;
+
+// GNU extensions.
+pub const CFA_GNU_window_save = 0x2d;
+pub const CFA_GNU_args_size = 0x2e;
+pub const CFA_GNU_negative_offset_extended = 0x2f;
+
+pub const CHILDREN_no = 0x00;
+pub const CHILDREN_yes = 0x01;
+
+pub const LNS_extended_op = 0x00;
+pub const LNS_copy = 0x01;
+pub const LNS_advance_pc = 0x02;
+pub const LNS_advance_line = 0x03;
+pub const LNS_set_file = 0x04;
+pub const LNS_set_column = 0x05;
+pub const LNS_negate_stmt = 0x06;
+pub const LNS_set_basic_block = 0x07;
+pub const LNS_const_add_pc = 0x08;
+pub const LNS_fixed_advance_pc = 0x09;
+pub const LNS_set_prologue_end = 0x0a;
+pub const LNS_set_epilogue_begin = 0x0b;
+pub const LNS_set_isa = 0x0c;
+
+pub const LNE_end_sequence = 0x01;
+pub const LNE_set_address = 0x02;
+pub const LNE_define_file = 0x03;
+pub const LNE_set_discriminator = 0x04;
+pub const LNE_lo_user = 0x80;
+pub const LNE_hi_user = 0xff;
+
+pub const LANG_C89 = 0x0001;
+pub const LANG_C = 0x0002;
+pub const LANG_Ada83 = 0x0003;
+pub const LANG_C_plus_plus = 0x0004;
+pub const LANG_Cobol74 = 0x0005;
+pub const LANG_Cobol85 = 0x0006;
+pub const LANG_Fortran77 = 0x0007;
+pub const LANG_Fortran90 = 0x0008;
+pub const LANG_Pascal83 = 0x0009;
+pub const LANG_Modula2 = 0x000a;
+pub const LANG_Java = 0x000b;
+pub const LANG_C99 = 0x000c;
+pub const LANG_Ada95 = 0x000d;
+pub const LANG_Fortran95 = 0x000e;
+pub const LANG_PLI = 0x000f;
+pub const LANG_ObjC = 0x0010;
+pub const LANG_ObjC_plus_plus = 0x0011;
+pub const LANG_UPC = 0x0012;
+pub const LANG_D = 0x0013;
+pub const LANG_Python = 0x0014;
+pub const LANG_Go = 0x0016;
+pub const LANG_C_plus_plus_11 = 0x001a;
+pub const LANG_Rust = 0x001c;
+pub const LANG_C11 = 0x001d;
+pub const LANG_C_plus_plus_14 = 0x0021;
+pub const LANG_Fortran03 = 0x0022;
+pub const LANG_Fortran08 = 0x0023;
+pub const LANG_lo_user = 0x8000;
+pub const LANG_hi_user = 0xffff;
+pub const LANG_Mips_Assembler = 0x8001;
+pub const LANG_Upc = 0x8765;
+pub const LANG_HP_Bliss = 0x8003;
+pub const LANG_HP_Basic91 = 0x8004;
+pub const LANG_HP_Pascal91 = 0x8005;
+pub const LANG_HP_IMacro = 0x8006;
+pub const LANG_HP_Assembler = 0x8007;
diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig
index 409dace20f..0d14f8d032 100644
--- a/lib/std/dynamic_library.zig
+++ b/lib/std/dynamic_library.zig
@@ -11,7 +11,7 @@ const system = std.os.system;
const maxInt = std.math.maxInt;
const max = std.math.max;
-pub const DynLib = switch (builtin.os) {
+pub const DynLib = switch (builtin.os.tag) {
.linux => if (builtin.link_libc) DlDynlib else ElfDynLib,
.windows => WindowsDynLib,
.macosx, .tvos, .watchos, .ios, .freebsd => DlDynlib,
@@ -82,12 +82,12 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator {
for (dyn_table) |*dyn| {
switch (dyn.d_tag) {
elf.DT_DEBUG => {
- const r_debug = @intToPtr(*RDebug, dyn.d_un.d_ptr);
+ const r_debug = @intToPtr(*RDebug, dyn.d_val);
if (r_debug.r_version != 1) return error.InvalidExe;
break :init r_debug.r_map;
},
elf.DT_PLTGOT => {
- const got_table = @intToPtr([*]usize, dyn.d_un.d_ptr);
+ const got_table = @intToPtr([*]usize, dyn.d_val);
// The address to the link_map structure is stored in the
// second slot
break :init @intToPtr(?*LinkMap, got_table[1]);
@@ -390,7 +390,7 @@ pub const DlDynlib = struct {
};
test "dynamic_library" {
- const libname = switch (builtin.os) {
+ const libname = switch (builtin.os.tag) {
.linux, .freebsd => "invalid_so.so",
.windows => "invalid_dll.dll",
.macosx, .tvos, .watchos, .ios => "invalid_dylib.dylib",
diff --git a/lib/std/elf.zig b/lib/std/elf.zig
index 9e4c1ac5f6..fc57db7c98 100644
--- a/lib/std/elf.zig
+++ b/lib/std/elf.zig
@@ -349,16 +349,6 @@ pub const Elf = struct {
program_headers: []ProgramHeader,
allocator: *mem.Allocator,
- /// Call close when done.
- pub fn openPath(allocator: *mem.Allocator, path: []const u8) !Elf {
- @compileError("TODO implement");
- }
-
- /// Call close when done.
- pub fn openFile(allocator: *mem.Allocator, file: File) !Elf {
- @compileError("TODO implement");
- }
-
pub fn openStream(
allocator: *mem.Allocator,
seekable_stream: *io.SeekableStream(anyerror, anyerror),
@@ -380,8 +370,8 @@ pub const Elf = struct {
};
elf.endian = switch (try in.readByte()) {
- 1 => builtin.Endian.Little,
- 2 => builtin.Endian.Big,
+ 1 => .Little,
+ 2 => .Big,
else => return error.InvalidFormat,
};
@@ -554,6 +544,21 @@ pub const Elf = struct {
};
pub const EI_NIDENT = 16;
+
+pub const EI_CLASS = 4;
+pub const ELFCLASSNONE = 0;
+pub const ELFCLASS32 = 1;
+pub const ELFCLASS64 = 2;
+pub const ELFCLASSNUM = 3;
+
+pub const EI_DATA = 5;
+pub const ELFDATANONE = 0;
+pub const ELFDATA2LSB = 1;
+pub const ELFDATA2MSB = 2;
+pub const ELFDATANUM = 3;
+
+pub const EI_VERSION = 6;
+
pub const Elf32_Half = u16;
pub const Elf64_Half = u16;
pub const Elf32_Word = u32;
@@ -703,17 +708,11 @@ pub const Elf64_Rela = extern struct {
};
pub const Elf32_Dyn = extern struct {
d_tag: Elf32_Sword,
- d_un: extern union {
- d_val: Elf32_Word,
- d_ptr: Elf32_Addr,
- },
+ d_val: Elf32_Addr,
};
pub const Elf64_Dyn = extern struct {
d_tag: Elf64_Sxword,
- d_un: extern union {
- d_val: Elf64_Xword,
- d_ptr: Elf64_Addr,
- },
+ d_val: Elf64_Addr,
};
pub const Elf32_Verdef = extern struct {
vd_version: Elf32_Half,
diff --git a/lib/std/event/channel.zig b/lib/std/event/channel.zig
index fd70f73aab..3c5b48d047 100644
--- a/lib/std/event/channel.zig
+++ b/lib/std/event/channel.zig
@@ -273,7 +273,7 @@ test "std.event.Channel" {
if (builtin.single_threaded) return error.SkipZigTest;
// https://github.com/ziglang/zig/issues/3251
- if (builtin.os == .freebsd) return error.SkipZigTest;
+ if (builtin.os.tag == .freebsd) return error.SkipZigTest;
var channel: Channel(i32) = undefined;
channel.init(&[0]i32{});
diff --git a/lib/std/event/future.zig b/lib/std/event/future.zig
index 492582da75..51a63e90ee 100644
--- a/lib/std/event/future.zig
+++ b/lib/std/event/future.zig
@@ -86,7 +86,7 @@ test "std.event.Future" {
// https://github.com/ziglang/zig/issues/1908
if (builtin.single_threaded) return error.SkipZigTest;
// https://github.com/ziglang/zig/issues/3251
- if (builtin.os == .freebsd) return error.SkipZigTest;
+ if (builtin.os.tag == .freebsd) return error.SkipZigTest;
// TODO provide a way to run tests in evented I/O mode
if (!std.io.is_async) return error.SkipZigTest;
diff --git a/lib/std/event/lock.zig b/lib/std/event/lock.zig
index e1b3495e5c..b9cbb5d95f 100644
--- a/lib/std/event/lock.zig
+++ b/lib/std/event/lock.zig
@@ -123,7 +123,7 @@ test "std.event.Lock" {
if (builtin.single_threaded) return error.SkipZigTest;
// TODO https://github.com/ziglang/zig/issues/3251
- if (builtin.os == .freebsd) return error.SkipZigTest;
+ if (builtin.os.tag == .freebsd) return error.SkipZigTest;
var lock = Lock.init();
defer lock.deinit();
diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig
index 085e56fc15..80ba5a79b5 100644
--- a/lib/std/event/loop.zig
+++ b/lib/std/event/loop.zig
@@ -34,7 +34,7 @@ pub const Loop = struct {
handle: anyframe,
overlapped: Overlapped,
- pub const overlapped_init = switch (builtin.os) {
+ pub const overlapped_init = switch (builtin.os.tag) {
.windows => windows.OVERLAPPED{
.Internal = 0,
.InternalHigh = 0,
@@ -52,7 +52,7 @@ pub const Loop = struct {
EventFd,
};
- pub const EventFd = switch (builtin.os) {
+ pub const EventFd = switch (builtin.os.tag) {
.macosx, .freebsd, .netbsd, .dragonfly => KEventFd,
.linux => struct {
base: ResumeNode,
@@ -71,7 +71,7 @@ pub const Loop = struct {
kevent: os.Kevent,
};
- pub const Basic = switch (builtin.os) {
+ pub const Basic = switch (builtin.os.tag) {
.macosx, .freebsd, .netbsd, .dragonfly => KEventBasic,
.linux => struct {
base: ResumeNode,
@@ -173,7 +173,7 @@ pub const Loop = struct {
const wakeup_bytes = [_]u8{0x1} ** 8;
fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.linux => {
self.os_data.fs_queue = std.atomic.Queue(Request).init();
self.os_data.fs_queue_item = 0;
@@ -404,7 +404,7 @@ pub const Loop = struct {
}
fn deinitOsData(self: *Loop) void {
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.linux => {
noasync os.close(self.os_data.final_eventfd);
while (self.available_eventfd_resume_nodes.pop()) |node| noasync os.close(node.data.eventfd);
@@ -568,7 +568,7 @@ pub const Loop = struct {
};
const eventfd_node = &resume_stack_node.data;
eventfd_node.base.handle = next_tick_node.data;
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.macosx, .freebsd, .netbsd, .dragonfly => {
const kevent_array = @as(*const [1]os.Kevent, &eventfd_node.kevent);
const empty_kevs = &[0]os.Kevent{};
@@ -628,7 +628,7 @@ pub const Loop = struct {
self.workerRun();
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.linux,
.macosx,
.freebsd,
@@ -678,7 +678,7 @@ pub const Loop = struct {
const prev = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
if (prev == 1) {
// cause all the threads to stop
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.linux => {
self.posixFsRequest(&self.os_data.fs_end_request);
// writing 8 bytes to an eventfd cannot fail
@@ -902,7 +902,7 @@ pub const Loop = struct {
self.finishOneEvent();
}
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.linux => {
// only process 1 event so we don't steal from other threads
var events: [1]os.linux.epoll_event = undefined;
@@ -989,7 +989,7 @@ pub const Loop = struct {
fn posixFsRequest(self: *Loop, request_node: *Request.Node) void {
self.beginOneEvent(); // finished in posixFsRun after processing the msg
self.os_data.fs_queue.put(request_node);
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.macosx, .freebsd, .netbsd, .dragonfly => {
const fs_kevs = @as(*const [1]os.Kevent, &self.os_data.fs_kevent_wake);
const empty_kevs = &[0]os.Kevent{};
@@ -1018,7 +1018,7 @@ pub const Loop = struct {
// https://github.com/ziglang/zig/issues/3157
fn posixFsRun(self: *Loop) void {
while (true) {
- if (builtin.os == .linux) {
+ if (builtin.os.tag == .linux) {
@atomicStore(i32, &self.os_data.fs_queue_item, 0, .SeqCst);
}
while (self.os_data.fs_queue.get()) |node| {
@@ -1053,7 +1053,7 @@ pub const Loop = struct {
}
self.finishOneEvent();
}
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.linux => {
const rc = os.linux.futex_wait(&self.os_data.fs_queue_item, os.linux.FUTEX_WAIT, 0, null);
switch (os.linux.getErrno(rc)) {
@@ -1071,7 +1071,7 @@ pub const Loop = struct {
}
}
- const OsData = switch (builtin.os) {
+ const OsData = switch (builtin.os.tag) {
.linux => LinuxOsData,
.macosx, .freebsd, .netbsd, .dragonfly => KEventData,
.windows => struct {
diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig
index effb18babd..38371b3aea 100644
--- a/lib/std/fmt.zig
+++ b/lib/std/fmt.zig
@@ -414,10 +414,9 @@ pub fn formatType(
if (max_depth == 0) {
return output(context, "{ ... }");
}
- comptime var field_i = 0;
try output(context, "{");
- inline for (StructT.fields) |f| {
- if (field_i == 0) {
+ inline for (StructT.fields) |f, i| {
+ if (i == 0) {
try output(context, " .");
} else {
try output(context, ", .");
@@ -425,7 +424,6 @@ pub fn formatType(
try output(context, f.name);
try output(context, " = ");
try formatType(@field(value, f.name), fmt, options, context, Errors, output, max_depth - 1);
- field_i += 1;
}
try output(context, " }");
},
@@ -443,10 +441,12 @@ pub fn formatType(
else => return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }),
},
.Many, .C => {
+ if (ptr_info.sentinel) |sentinel| {
+ return formatType(mem.span(value), fmt, options, context, Errors, output, max_depth);
+ }
if (ptr_info.child == u8) {
if (fmt.len > 0 and fmt[0] == 's') {
- const len = mem.len(u8, value);
- return formatText(value[0..len], fmt, options, context, Errors, output);
+ return formatText(mem.span(value), fmt, options, context, Errors, output);
}
}
return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
index c3d418eb8c..5077c52cd9 100644
--- a/lib/std/fs.zig
+++ b/lib/std/fs.zig
@@ -29,7 +29,7 @@ pub const Watch = @import("fs/watch.zig").Watch;
/// All file system operations which return a path are guaranteed to
/// fit into a UTF-8 encoded array of this length.
/// The byte count includes room for a null sentinel byte.
-pub const MAX_PATH_BYTES = switch (builtin.os) {
+pub const MAX_PATH_BYTES = switch (builtin.os.tag) {
.linux, .macosx, .ios, .freebsd, .netbsd, .dragonfly => os.PATH_MAX,
// Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
// If it would require 4 UTF-8 bytes, then there would be a surrogate
@@ -47,7 +47,7 @@ pub const base64_encoder = base64.Base64Encoder.init(
/// Whether or not async file system syscalls need a dedicated thread because the operating
/// system does not support non-blocking I/O on the file system.
-pub const need_async_thread = std.io.is_async and switch (builtin.os) {
+pub const need_async_thread = std.io.is_async and switch (builtin.os.tag) {
.windows, .other => false,
else => true,
};
@@ -270,7 +270,7 @@ pub const AtomicFile = struct {
assert(!self.finished);
self.file.close();
self.finished = true;
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_path);
const tmp_path_w = try os.windows.cStrToPrefixedFileW(@ptrCast([*:0]u8, &self.tmp_path_buf));
return os.renameW(&tmp_path_w, &dest_path_w);
@@ -394,7 +394,7 @@ pub const Dir = struct {
const IteratorError = error{AccessDenied} || os.UnexpectedError;
- pub const Iterator = switch (builtin.os) {
+ pub const Iterator = switch (builtin.os.tag) {
.macosx, .ios, .freebsd, .netbsd, .dragonfly => struct {
dir: Dir,
seek: i64,
@@ -409,7 +409,7 @@ pub const Dir = struct {
/// Memory such as file names referenced in this returned entry becomes invalid
/// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
pub fn next(self: *Self) Error!?Entry {
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.macosx, .ios => return self.nextDarwin(),
.freebsd, .netbsd, .dragonfly => return self.nextBsd(),
else => @compileError("unimplemented"),
@@ -644,7 +644,7 @@ pub const Dir = struct {
};
pub fn iterate(self: Dir) Iterator {
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.macosx, .ios, .freebsd, .netbsd, .dragonfly => return Iterator{
.dir = self,
.seek = 0,
@@ -710,7 +710,7 @@ pub const Dir = struct {
/// Asserts that the path parameter has no null bytes.
pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.openFileW(&path_w, flags);
}
@@ -720,7 +720,7 @@ pub const Dir = struct {
/// Same as `openFile` but the path parameter is null-terminated.
pub fn openFileC(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const path_w = try os.windows.cStrToPrefixedFileW(sub_path);
return self.openFileW(&path_w, flags);
}
@@ -731,11 +731,18 @@ pub const Dir = struct {
@as(u32, os.O_WRONLY)
else
@as(u32, os.O_RDONLY);
- const fd = if (need_async_thread)
+ const fd = if (need_async_thread and !flags.always_blocking)
try std.event.Loop.instance.?.openatZ(self.fd, sub_path, os_flags, 0)
else
try os.openatC(self.fd, sub_path, os_flags, 0);
- return File{ .handle = fd, .io_mode = .blocking };
+ return File{
+ .handle = fd,
+ .io_mode = .blocking,
+ .async_block_allowed = if (flags.always_blocking)
+ File.async_block_allowed_yes
+ else
+ File.async_block_allowed_no,
+ };
}
/// Same as `openFile` but Windows-only and the path parameter is
@@ -753,7 +760,7 @@ pub const Dir = struct {
/// Asserts that the path parameter has no null bytes.
pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.createFileW(&path_w, flags);
}
@@ -763,7 +770,7 @@ pub const Dir = struct {
/// Same as `createFile` but the path parameter is null-terminated.
pub fn createFileC(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
return self.createFileW(&path_w, flags);
}
@@ -894,7 +901,7 @@ pub const Dir = struct {
/// Asserts that the path parameter has no null bytes.
pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir {
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.openDirTraverseW(&sub_path_w);
}
@@ -912,7 +919,7 @@ pub const Dir = struct {
/// Asserts that the path parameter has no null bytes.
pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir {
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.openDirListW(&sub_path_w);
}
@@ -923,7 +930,7 @@ pub const Dir = struct {
/// Same as `openDirTraverse` except the parameter is null-terminated.
pub fn openDirTraverseC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
return self.openDirTraverseW(&sub_path_w);
} else {
@@ -934,7 +941,7 @@ pub const Dir = struct {
/// Same as `openDirList` except the parameter is null-terminated.
pub fn openDirListC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
return self.openDirListW(&sub_path_w);
} else {
@@ -1076,7 +1083,7 @@ pub const Dir = struct {
/// Asserts that the path parameter has no null bytes.
pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.deleteDirW(&sub_path_w);
}
@@ -1333,7 +1340,7 @@ pub const Dir = struct {
/// For example, instead of testing if a file exists and then opening it, just
/// open it and handle the error for file not found.
pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.accessW(&sub_path_w, flags);
}
@@ -1343,7 +1350,7 @@ pub const Dir = struct {
/// Same as `access` except the path parameter is null-terminated.
pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path);
return self.accessW(&sub_path_w, flags);
}
@@ -1374,7 +1381,7 @@ pub const Dir = struct {
/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
/// On POSIX targets, this function is comptime-callable.
pub fn cwd() Dir {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
} else {
return Dir{ .fd = os.AT_FDCWD };
@@ -1553,10 +1560,10 @@ pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError;
pub fn openSelfExe() OpenSelfExeError!File {
- if (builtin.os == .linux) {
+ if (builtin.os.tag == .linux) {
return openFileAbsoluteC("/proc/self/exe", .{});
}
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const wide_slice = selfExePathW();
const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice);
return cwd().openReadW(&prefixed_path_w);
@@ -1568,7 +1575,7 @@ pub fn openSelfExe() OpenSelfExeError!File {
}
test "openSelfExe" {
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.linux, .macosx, .ios, .windows, .freebsd, .dragonfly => (try openSelfExe()).close(),
else => return error.SkipZigTest, // Unsupported OS.
}
@@ -1593,7 +1600,7 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 {
if (rc != 0) return error.NameTooLong;
return mem.toSlice(u8, @ptrCast([*:0]u8, out_buffer));
}
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.linux => return os.readlinkC("/proc/self/exe", out_buffer),
.freebsd, .dragonfly => {
var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC, os.KERN_PROC_PATHNAME, -1 };
@@ -1635,7 +1642,7 @@ pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 {
/// Get the directory path that contains the current executable.
/// Returned value is a slice of out_buffer.
pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const u8 {
- if (builtin.os == .linux) {
+ if (builtin.os.tag == .linux) {
// If the currently executing binary has been deleted,
// the file path looks something like `/a/b/c/exe (deleted)`
// This path cannot be opened, but it's valid for determining the directory
diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig
index a3a428e77b..c243eeb62c 100644
--- a/lib/std/fs/file.zig
+++ b/lib/std/fs/file.zig
@@ -20,7 +20,7 @@ pub const File = struct {
/// or, more specifically, whether the I/O is blocking.
io_mode: io.Mode,
- /// Even when std.io.mode is async, it is still sometimes desirable to perform blocking I/O, although
+ /// Even when 'std.io.mode' is async, it is still sometimes desirable to perform blocking I/O, although
/// not by default. For example, when printing a stack trace to stderr.
async_block_allowed: @TypeOf(async_block_allowed_no) = async_block_allowed_no,
@@ -29,7 +29,7 @@ pub const File = struct {
pub const Mode = os.mode_t;
- pub const default_mode = switch (builtin.os) {
+ pub const default_mode = switch (builtin.os.tag) {
.windows => 0,
else => 0o666,
};
@@ -40,6 +40,11 @@ pub const File = struct {
pub const OpenFlags = struct {
read: bool = true,
write: bool = false,
+
+ /// This prevents `O_NONBLOCK` from being passed even if `std.io.is_async`.
+ /// It allows the use of `noasync` when calling functions related to opening
+ /// the file, reading, and writing.
+ always_blocking: bool = false,
};
/// TODO https://github.com/ziglang/zig/issues/3802
@@ -78,7 +83,7 @@ pub const File = struct {
/// Test whether ANSI escape codes will be treated as such.
pub fn supportsAnsiEscapeCodes(self: File) bool {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return os.isCygwinPty(self.handle);
}
if (self.isTty()) {
@@ -123,7 +128,7 @@ pub const File = struct {
/// TODO: integrate with async I/O
pub fn getEndPos(self: File) GetPosError!u64 {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return windows.GetFileSizeEx(self.handle);
}
return (try self.stat()).size;
@@ -133,7 +138,7 @@ pub const File = struct {
/// TODO: integrate with async I/O
pub fn mode(self: File) ModeError!Mode {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return {};
}
return (try self.stat()).mode;
@@ -157,7 +162,7 @@ pub const File = struct {
/// TODO: integrate with async I/O
pub fn stat(self: File) StatError!Stat {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
var info: windows.FILE_ALL_INFORMATION = undefined;
const rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &info, @sizeOf(windows.FILE_ALL_INFORMATION), .FileAllInformation);
@@ -204,7 +209,7 @@ pub const File = struct {
/// last modification timestamp in nanoseconds
mtime: i64,
) UpdateTimesError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const atime_ft = windows.nanoSecondsToFileTime(atime);
const mtime_ft = windows.nanoSecondsToFileTime(mtime);
return windows.SetFileTime(self.handle, null, &atime_ft, &mtime_ft);
diff --git a/lib/std/fs/get_app_data_dir.zig b/lib/std/fs/get_app_data_dir.zig
index 35c0265435..31aab590d8 100644
--- a/lib/std/fs/get_app_data_dir.zig
+++ b/lib/std/fs/get_app_data_dir.zig
@@ -13,7 +13,7 @@ pub const GetAppDataDirError = error{
/// Caller owns returned memory.
/// TODO determine if we can remove the allocator requirement
pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.windows => {
var dir_path_ptr: [*:0]u16 = undefined;
switch (os.windows.shell32.SHGetKnownFolderPath(
diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig
index 5d1c775629..35bc9b53b0 100644
--- a/lib/std/fs/path.zig
+++ b/lib/std/fs/path.zig
@@ -13,18 +13,18 @@ const process = std.process;
pub const sep_windows = '\\';
pub const sep_posix = '/';
-pub const sep = if (builtin.os == .windows) sep_windows else sep_posix;
+pub const sep = if (builtin.os.tag == .windows) sep_windows else sep_posix;
pub const sep_str_windows = "\\";
pub const sep_str_posix = "/";
-pub const sep_str = if (builtin.os == .windows) sep_str_windows else sep_str_posix;
+pub const sep_str = if (builtin.os.tag == .windows) sep_str_windows else sep_str_posix;
pub const delimiter_windows = ';';
pub const delimiter_posix = ':';
-pub const delimiter = if (builtin.os == .windows) delimiter_windows else delimiter_posix;
+pub const delimiter = if (builtin.os.tag == .windows) delimiter_windows else delimiter_posix;
pub fn isSep(byte: u8) bool {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return byte == '/' or byte == '\\';
} else {
return byte == '/';
@@ -74,7 +74,7 @@ fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u
return buf;
}
-pub const join = if (builtin.os == .windows) joinWindows else joinPosix;
+pub const join = if (builtin.os.tag == .windows) joinWindows else joinPosix;
/// Naively combines a series of paths with the native path seperator.
/// Allocates memory for the result, which must be freed by the caller.
@@ -129,7 +129,7 @@ test "join" {
}
pub fn isAbsoluteC(path_c: [*:0]const u8) bool {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return isAbsoluteWindowsC(path_c);
} else {
return isAbsolutePosixC(path_c);
@@ -137,7 +137,7 @@ pub fn isAbsoluteC(path_c: [*:0]const u8) bool {
}
pub fn isAbsolute(path: []const u8) bool {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return isAbsoluteWindows(path);
} else {
return isAbsolutePosix(path);
@@ -318,7 +318,7 @@ test "windowsParsePath" {
}
pub fn diskDesignator(path: []const u8) []const u8 {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return diskDesignatorWindows(path);
} else {
return "";
@@ -383,7 +383,7 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool {
/// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`.
pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return resolveWindows(allocator, paths);
} else {
return resolvePosix(allocator, paths);
@@ -400,7 +400,7 @@ pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 {
/// Without performing actual syscalls, resolving `..` could be incorrect.
pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
if (paths.len == 0) {
- assert(builtin.os == .windows); // resolveWindows called on non windows can't use getCwd
+ assert(builtin.os.tag == .windows); // resolveWindows called on non windows can't use getCwd
return process.getCwdAlloc(allocator);
}
@@ -495,7 +495,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
result_disk_designator = result[0..result_index];
},
WindowsPath.Kind.None => {
- assert(builtin.os == .windows); // resolveWindows called on non windows can't use getCwd
+ assert(builtin.os.tag == .windows); // resolveWindows called on non windows can't use getCwd
const cwd = try process.getCwdAlloc(allocator);
defer allocator.free(cwd);
const parsed_cwd = windowsParsePath(cwd);
@@ -510,7 +510,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
},
}
} else {
- assert(builtin.os == .windows); // resolveWindows called on non windows can't use getCwd
+ assert(builtin.os.tag == .windows); // resolveWindows called on non windows can't use getCwd
// TODO call get cwd for the result_disk_designator instead of the global one
const cwd = try process.getCwdAlloc(allocator);
defer allocator.free(cwd);
@@ -581,7 +581,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
/// Without performing actual syscalls, resolving `..` could be incorrect.
pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
if (paths.len == 0) {
- assert(builtin.os != .windows); // resolvePosix called on windows can't use getCwd
+ assert(builtin.os.tag != .windows); // resolvePosix called on windows can't use getCwd
return process.getCwdAlloc(allocator);
}
@@ -603,7 +603,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
if (have_abs) {
result = try allocator.alloc(u8, max_size);
} else {
- assert(builtin.os != .windows); // resolvePosix called on windows can't use getCwd
+ assert(builtin.os.tag != .windows); // resolvePosix called on windows can't use getCwd
const cwd = try process.getCwdAlloc(allocator);
defer allocator.free(cwd);
result = try allocator.alloc(u8, max_size + cwd.len + 1);
@@ -645,7 +645,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
test "resolve" {
const cwd = try process.getCwdAlloc(testing.allocator);
defer testing.allocator.free(cwd);
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) {
cwd[0] = asciiUpper(cwd[0]);
}
@@ -661,7 +661,7 @@ test "resolveWindows" {
// TODO https://github.com/ziglang/zig/issues/3288
return error.SkipZigTest;
}
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const cwd = try process.getCwdAlloc(testing.allocator);
defer testing.allocator.free(cwd);
const parsed_cwd = windowsParsePath(cwd);
@@ -732,7 +732,7 @@ fn testResolvePosix(paths: []const []const u8, expected: []const u8) !void {
/// If the path is a file in the current directory (no directory component)
/// then returns null
pub fn dirname(path: []const u8) ?[]const u8 {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return dirnameWindows(path);
} else {
return dirnamePosix(path);
@@ -864,7 +864,7 @@ fn testDirnameWindows(input: []const u8, expected_output: ?[]const u8) void {
}
pub fn basename(path: []const u8) []const u8 {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return basenameWindows(path);
} else {
return basenamePosix(path);
@@ -980,7 +980,7 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) void {
/// string is returned.
/// On Windows this canonicalizes the drive to a capital letter and paths to `\\`.
pub fn relative(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return relativeWindows(allocator, from, to);
} else {
return relativePosix(allocator, from, to);
diff --git a/lib/std/fs/watch.zig b/lib/std/fs/watch.zig
index 0ff8c47ecf..1eb5a97ff1 100644
--- a/lib/std/fs/watch.zig
+++ b/lib/std/fs/watch.zig
@@ -42,7 +42,7 @@ pub fn Watch(comptime V: type) type {
os_data: OsData,
allocator: *Allocator,
- const OsData = switch (builtin.os) {
+ const OsData = switch (builtin.os.tag) {
// TODO https://github.com/ziglang/zig/issues/3778
.macosx, .freebsd, .netbsd, .dragonfly => KqOsData,
.linux => LinuxOsData,
@@ -121,7 +121,7 @@ pub fn Watch(comptime V: type) type {
const self = try allocator.create(Self);
errdefer allocator.destroy(self);
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.linux => {
const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);
errdefer os.close(inotify_fd);
@@ -172,7 +172,7 @@ pub fn Watch(comptime V: type) type {
/// All addFile calls and removeFile calls must have completed.
pub fn deinit(self: *Self) void {
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.macosx, .freebsd, .netbsd, .dragonfly => {
// TODO we need to cancel the frames before destroying the lock
self.os_data.table_lock.deinit();
@@ -223,7 +223,7 @@ pub fn Watch(comptime V: type) type {
}
pub fn addFile(self: *Self, file_path: []const u8, value: V) !?V {
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.macosx, .freebsd, .netbsd, .dragonfly => return addFileKEvent(self, file_path, value),
.linux => return addFileLinux(self, file_path, value),
.windows => return addFileWindows(self, file_path, value),
diff --git a/lib/std/hash/auto_hash.zig b/lib/std/hash/auto_hash.zig
index 1bd7dfc05b..2c6b37e3db 100644
--- a/lib/std/hash/auto_hash.zig
+++ b/lib/std/hash/auto_hash.zig
@@ -25,13 +25,13 @@ pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void {
const info = @typeInfo(@TypeOf(key));
switch (info.Pointer.size) {
- builtin.TypeInfo.Pointer.Size.One => switch (strat) {
+ .One => switch (strat) {
.Shallow => hash(hasher, @ptrToInt(key), .Shallow),
.Deep => hash(hasher, key.*, .Shallow),
.DeepRecursive => hash(hasher, key.*, .DeepRecursive),
},
- builtin.TypeInfo.Pointer.Size.Slice => switch (strat) {
+ .Slice => switch (strat) {
.Shallow => {
hashPointer(hasher, key.ptr, .Shallow);
hash(hasher, key.len, .Shallow);
@@ -40,9 +40,7 @@ pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void {
.DeepRecursive => hashArray(hasher, key, .DeepRecursive),
},
- builtin.TypeInfo.Pointer.Size.Many,
- builtin.TypeInfo.Pointer.Size.C,
- => switch (strat) {
+ .Many, .C, => switch (strat) {
.Shallow => hash(hasher, @ptrToInt(key), .Shallow),
else => @compileError(
\\ unknown-length pointers and C pointers cannot be hashed deeply.
diff --git a/lib/std/hash/benchmark.zig b/lib/std/hash/benchmark.zig
index ed1bab9d87..4762b65afb 100644
--- a/lib/std/hash/benchmark.zig
+++ b/lib/std/hash/benchmark.zig
@@ -168,7 +168,7 @@ fn usage() void {
}
fn mode(comptime x: comptime_int) comptime_int {
- return if (builtin.mode == builtin.Mode.Debug) x / 64 else x;
+ return if (builtin.mode == .Debug) x / 64 else x;
}
pub fn main() !void {
diff --git a/lib/std/hash/cityhash.zig b/lib/std/hash/cityhash.zig
index 18b3abe16d..a717303090 100644
--- a/lib/std/hash/cityhash.zig
+++ b/lib/std/hash/cityhash.zig
@@ -11,7 +11,7 @@ pub const CityHash32 = struct {
fn fetch32(ptr: [*]const u8) u32 {
var v: u32 = undefined;
@memcpy(@ptrCast([*]u8, &v), ptr, 4);
- if (builtin.endian == builtin.Endian.Big)
+ if (builtin.endian == .Big)
return @byteSwap(u32, v);
return v;
}
@@ -174,7 +174,7 @@ pub const CityHash64 = struct {
fn fetch32(ptr: [*]const u8) u32 {
var v: u32 = undefined;
@memcpy(@ptrCast([*]u8, &v), ptr, 4);
- if (builtin.endian == builtin.Endian.Big)
+ if (builtin.endian == .Big)
return @byteSwap(u32, v);
return v;
}
@@ -182,7 +182,7 @@ pub const CityHash64 = struct {
fn fetch64(ptr: [*]const u8) u64 {
var v: u64 = undefined;
@memcpy(@ptrCast([*]u8, &v), ptr, 8);
- if (builtin.endian == builtin.Endian.Big)
+ if (builtin.endian == .Big)
return @byteSwap(u64, v);
return v;
}
@@ -369,7 +369,7 @@ fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 {
key[i] = @intCast(u8, i);
var h = hash_fn(key[0..i], 256 - i);
- if (builtin.endian == builtin.Endian.Big)
+ if (builtin.endian == .Big)
h = @byteSwap(@TypeOf(h), h);
@memcpy(@ptrCast([*]u8, &hashes[i * hashbytes]), @ptrCast([*]u8, &h), hashbytes);
}
diff --git a/lib/std/hash/murmur.zig b/lib/std/hash/murmur.zig
index 23b11ef284..96efc8b9c1 100644
--- a/lib/std/hash/murmur.zig
+++ b/lib/std/hash/murmur.zig
@@ -17,7 +17,7 @@ pub const Murmur2_32 = struct {
var h1: u32 = seed ^ len;
for (@ptrCast([*]align(1) const u32, str.ptr)[0..(len >> 2)]) |v| {
var k1: u32 = v;
- if (builtin.endian == builtin.Endian.Big)
+ if (builtin.endian == .Big)
k1 = @byteSwap(u32, k1);
k1 *%= m;
k1 ^= k1 >> 24;
@@ -102,7 +102,7 @@ pub const Murmur2_64 = struct {
var h1: u64 = seed ^ (len *% m);
for (@ptrCast([*]align(1) const u64, str.ptr)[0..@intCast(usize, len >> 3)]) |v| {
var k1: u64 = v;
- if (builtin.endian == builtin.Endian.Big)
+ if (builtin.endian == .Big)
k1 = @byteSwap(u64, k1);
k1 *%= m;
k1 ^= k1 >> 47;
@@ -115,7 +115,7 @@ pub const Murmur2_64 = struct {
if (rest > 0) {
var k1: u64 = 0;
@memcpy(@ptrCast([*]u8, &k1), @ptrCast([*]const u8, &str[@intCast(usize, offset)]), @intCast(usize, rest));
- if (builtin.endian == builtin.Endian.Big)
+ if (builtin.endian == .Big)
k1 = @byteSwap(u64, k1);
h1 ^= k1;
h1 *%= m;
@@ -182,7 +182,7 @@ pub const Murmur3_32 = struct {
var h1: u32 = seed;
for (@ptrCast([*]align(1) const u32, str.ptr)[0..(len >> 2)]) |v| {
var k1: u32 = v;
- if (builtin.endian == builtin.Endian.Big)
+ if (builtin.endian == .Big)
k1 = @byteSwap(u32, k1);
k1 *%= c1;
k1 = rotl32(k1, 15);
@@ -294,7 +294,7 @@ fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 {
key[i] = @truncate(u8, i);
var h = hash_fn(key[0..i], 256 - i);
- if (builtin.endian == builtin.Endian.Big)
+ if (builtin.endian == .Big)
h = @byteSwap(@TypeOf(h), h);
@memcpy(@ptrCast([*]u8, &hashes[i * hashbytes]), @ptrCast([*]u8, &h), hashbytes);
}
@@ -308,7 +308,7 @@ test "murmur2_32" {
var v1: u64 = 0x1234567812345678;
var v0le: u32 = v0;
var v1le: u64 = v1;
- if (builtin.endian == builtin.Endian.Big) {
+ if (builtin.endian == .Big) {
v0le = @byteSwap(u32, v0le);
v1le = @byteSwap(u64, v1le);
}
@@ -322,7 +322,7 @@ test "murmur2_64" {
var v1: u64 = 0x1234567812345678;
var v0le: u32 = v0;
var v1le: u64 = v1;
- if (builtin.endian == builtin.Endian.Big) {
+ if (builtin.endian == .Big) {
v0le = @byteSwap(u32, v0le);
v1le = @byteSwap(u64, v1le);
}
@@ -336,7 +336,7 @@ test "murmur3_32" {
var v1: u64 = 0x1234567812345678;
var v0le: u32 = v0;
var v1le: u64 = v1;
- if (builtin.endian == builtin.Endian.Big) {
+ if (builtin.endian == .Big) {
v0le = @byteSwap(u32, v0le);
v1le = @byteSwap(u64, v1le);
}
diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig
index 80ca218df6..4b3e082f0c 100644
--- a/lib/std/hash_map.zig
+++ b/lib/std/hash_map.zig
@@ -10,7 +10,7 @@ const Wyhash = std.hash.Wyhash;
const Allocator = mem.Allocator;
const builtin = @import("builtin");
-const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast;
+const want_modification_safety = builtin.mode != .ReleaseFast;
const debug_u32 = if (want_modification_safety) u32 else void;
pub fn AutoHashMap(comptime K: type, comptime V: type) type {
diff --git a/lib/std/heap.zig b/lib/std/heap.zig
index 4295f1393d..65809e97b4 100644
--- a/lib/std/heap.zig
+++ b/lib/std/heap.zig
@@ -36,7 +36,7 @@ fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new
/// Thread-safe and lock-free.
pub const page_allocator = if (std.Target.current.isWasm())
&wasm_page_allocator_state
-else if (std.Target.current.getOs() == .freestanding)
+else if (std.Target.current.os.tag == .freestanding)
root.os.heap.page_allocator
else
&page_allocator_state;
@@ -57,7 +57,7 @@ const PageAllocator = struct {
fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {
if (n == 0) return &[0]u8{};
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const w = os.windows;
// Although officially it's at least aligned to page boundary,
@@ -143,7 +143,7 @@ const PageAllocator = struct {
fn shrink(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
const old_mem = @alignCast(mem.page_size, old_mem_unaligned);
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const w = os.windows;
if (new_size == 0) {
// From the docs:
@@ -183,7 +183,7 @@ const PageAllocator = struct {
fn realloc(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
const old_mem = @alignCast(mem.page_size, old_mem_unaligned);
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
if (old_mem.len == 0) {
return alloc(allocator, new_size, new_align);
}
@@ -412,7 +412,7 @@ const WasmPageAllocator = struct {
}
};
-pub const HeapAllocator = switch (builtin.os) {
+pub const HeapAllocator = switch (builtin.os.tag) {
.windows => struct {
allocator: Allocator,
heap_handle: ?HeapHandle,
@@ -855,7 +855,7 @@ test "PageAllocator" {
try testAllocatorAlignedShrink(allocator);
}
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
// Trying really large alignment. As mentionned in the implementation,
// VirtualAlloc returns 64K aligned addresses. We want to make sure
// PageAllocator works beyond that, as it's not tested by
@@ -868,7 +868,7 @@ test "PageAllocator" {
}
test "HeapAllocator" {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
var heap_allocator = HeapAllocator.init();
defer heap_allocator.deinit();
diff --git a/lib/std/io.zig b/lib/std/io.zig
index 548f119b4f..d0bf26d548 100644
--- a/lib/std/io.zig
+++ b/lib/std/io.zig
@@ -35,7 +35,7 @@ else
pub const is_async = mode != .blocking;
fn getStdOutHandle() os.fd_t {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return os.windows.peb().ProcessParameters.hStdOutput;
}
@@ -54,7 +54,7 @@ pub fn getStdOut() File {
}
fn getStdErrHandle() os.fd_t {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return os.windows.peb().ProcessParameters.hStdError;
}
@@ -74,7 +74,7 @@ pub fn getStdErr() File {
}
fn getStdInHandle() os.fd_t {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return os.windows.peb().ProcessParameters.hStdInput;
}
@@ -348,11 +348,11 @@ pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type {
const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count;
const shift = u7_bit_count - n;
switch (endian) {
- builtin.Endian.Big => {
+ .Big => {
out_buffer = @as(Buf, self.bit_buffer >> shift);
self.bit_buffer <<= n;
},
- builtin.Endian.Little => {
+ .Little => {
const value = (self.bit_buffer << shift) >> shift;
out_buffer = @as(Buf, value);
self.bit_buffer >>= n;
@@ -376,7 +376,7 @@ pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type {
};
switch (endian) {
- builtin.Endian.Big => {
+ .Big => {
if (n >= u8_bit_count) {
out_buffer <<= @intCast(u3, u8_bit_count - 1);
out_buffer <<= 1;
@@ -392,7 +392,7 @@ pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type {
self.bit_buffer = @truncate(u7, next_byte << @intCast(u3, n - 1));
self.bit_count = shift;
},
- builtin.Endian.Little => {
+ .Little => {
if (n >= u8_bit_count) {
out_buffer |= @as(Buf, next_byte) << @intCast(BufShift, out_bits.*);
out_bits.* += u8_bit_count;
@@ -666,8 +666,8 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count);
var in_buffer = switch (endian) {
- builtin.Endian.Big => buf_value << @intCast(BufShift, buf_bit_count - bits),
- builtin.Endian.Little => buf_value,
+ .Big => buf_value << @intCast(BufShift, buf_bit_count - bits),
+ .Little => buf_value,
};
var in_bits = bits;
@@ -675,13 +675,13 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
const bits_remaining = u8_bit_count - self.bit_count;
const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining);
switch (endian) {
- builtin.Endian.Big => {
+ .Big => {
const shift = @intCast(BufShift, high_byte_shift + self.bit_count);
const v = @intCast(u8, in_buffer >> shift);
self.bit_buffer |= v;
in_buffer <<= n;
},
- builtin.Endian.Little => {
+ .Little => {
const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count);
self.bit_buffer |= v;
in_buffer >>= n;
@@ -701,13 +701,13 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
//copy bytes until we can't fill one anymore, then leave the rest in bit_buffer
while (in_bits >= u8_bit_count) {
switch (endian) {
- builtin.Endian.Big => {
+ .Big => {
const v = @intCast(u8, in_buffer >> high_byte_shift);
try self.out_stream.writeByte(v);
in_buffer <<= @intCast(u3, u8_bit_count - 1);
in_buffer <<= 1;
},
- builtin.Endian.Little => {
+ .Little => {
const v = @truncate(u8, in_buffer);
try self.out_stream.writeByte(v);
in_buffer >>= @intCast(u3, u8_bit_count - 1);
@@ -720,8 +720,8 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
if (in_bits > 0) {
self.bit_count = @intCast(u4, in_bits);
self.bit_buffer = switch (endian) {
- builtin.Endian.Big => @truncate(u8, in_buffer >> high_byte_shift),
- builtin.Endian.Little => @truncate(u8, in_buffer),
+ .Big => @truncate(u8, in_buffer >> high_byte_shift),
+ .Little => @truncate(u8, in_buffer),
};
}
}
@@ -858,10 +858,10 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
var result = @as(U, 0);
for (buffer) |byte, i| {
switch (endian) {
- builtin.Endian.Big => {
+ .Big => {
result = (result << u8_bit_count) | byte;
},
- builtin.Endian.Little => {
+ .Little => {
result |= @as(U, byte) << @intCast(Log2U, u8_bit_count * i);
},
}
diff --git a/lib/std/io/seekable_stream.zig b/lib/std/io/seekable_stream.zig
index 48dc31b785..052abbc856 100644
--- a/lib/std/io/seekable_stream.zig
+++ b/lib/std/io/seekable_stream.zig
@@ -73,7 +73,7 @@ pub const SliceSeekableInStream = struct {
fn seekToFn(in_stream: *SeekableInStream, pos: u64) SeekError!void {
const self = @fieldParentPtr(Self, "seekable_stream", in_stream);
const usize_pos = @intCast(usize, pos);
- if (usize_pos >= self.slice.len) return error.EndOfStream;
+ if (usize_pos > self.slice.len) return error.EndOfStream;
self.pos = usize_pos;
}
@@ -86,7 +86,7 @@ pub const SliceSeekableInStream = struct {
self.pos -= abs_amt;
} else {
const usize_amt = @intCast(usize, amt);
- if (self.pos + usize_amt >= self.slice.len) return error.EndOfStream;
+ if (self.pos + usize_amt > self.slice.len) return error.EndOfStream;
self.pos += usize_amt;
}
}
diff --git a/lib/std/macho.zig b/lib/std/macho.zig
index e1bbd755c6..a499a93675 100644
--- a/lib/std/macho.zig
+++ b/lib/std/macho.zig
@@ -24,6 +24,17 @@ pub const load_command = extern struct {
cmdsize: u32,
};
+pub const uuid_command = extern struct {
+ /// LC_UUID
+ cmd: u32,
+
+ /// sizeof(struct uuid_command)
+ cmdsize: u32,
+
+ /// the 128-bit uuid
+ uuid: [16]u8,
+};
+
/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD
/// "stab" style symbol table information as described in the header files
/// and .
diff --git a/lib/std/math/pow.zig b/lib/std/math/pow.zig
index 4f623377e6..ac6aee4cce 100644
--- a/lib/std/math/pow.zig
+++ b/lib/std/math/pow.zig
@@ -32,7 +32,7 @@ const expect = std.testing.expect;
/// - pow(-inf, y) = pow(-0, -y)
/// - pow(x, y) = nan for finite x < 0 and finite non-integer y
pub fn pow(comptime T: type, x: T, y: T) T {
- if (@typeInfo(T) == builtin.TypeId.Int) {
+ if (@typeInfo(T) == .Int) {
return math.powi(T, x, y) catch unreachable;
}
diff --git a/lib/std/math/powi.zig b/lib/std/math/powi.zig
index d80700e5cd..ce3a3713e3 100644
--- a/lib/std/math/powi.zig
+++ b/lib/std/math/powi.zig
@@ -25,7 +25,7 @@ pub fn powi(comptime T: type, x: T, y: T) (error{
}!T) {
const info = @typeInfo(T);
- comptime assert(@typeInfo(T) == builtin.TypeId.Int);
+ comptime assert(@typeInfo(T) == .Int);
// powi(x, +-0) = 1 for any x
if (y == 0 or y == -0) {
diff --git a/lib/std/mem.zig b/lib/std/mem.zig
index a4b48bbc1c..391d587dbc 100644
--- a/lib/std/mem.zig
+++ b/lib/std/mem.zig
@@ -333,8 +333,20 @@ pub fn zeroes(comptime T: type) T {
}
return array;
},
- .Vector, .ErrorUnion, .ErrorSet, .Union, .Fn, .BoundFn, .Type, .NoReturn, .Undefined, .Opaque, .Frame, .AnyFrame, => {
- @compileError("Can't set a "++ @typeName(T) ++" to zero.");
+ .Vector,
+ .ErrorUnion,
+ .ErrorSet,
+ .Union,
+ .Fn,
+ .BoundFn,
+ .Type,
+ .NoReturn,
+ .Undefined,
+ .Opaque,
+ .Frame,
+ .AnyFrame,
+ => {
+ @compileError("Can't set a " ++ @typeName(T) ++ " to zero.");
},
}
}
@@ -470,18 +482,115 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
return true;
}
-pub fn len(comptime T: type, ptr: [*:0]const T) usize {
- var count: usize = 0;
- while (ptr[count] != 0) : (count += 1) {}
- return count;
-}
-
+/// Deprecated. Use `span`.
pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T {
- return ptr[0..len(T, ptr) :0];
+ return ptr[0..len(ptr) :0];
}
+/// Deprecated. Use `span`.
pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T {
- return ptr[0..len(T, ptr) :0];
+ return ptr[0..len(ptr) :0];
+}
+
+/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and
+/// returns a slice. If there is a sentinel on the input type, there will be a
+/// sentinel on the output type. The constness of the output type matches
+/// the constness of the input type. `[*c]` pointers are assumed to be 0-terminated,
+/// and assumed to not allow null.
+pub fn Span(comptime T: type) type {
+ var ptr_info = @typeInfo(T).Pointer;
+ switch (ptr_info.size) {
+ .One => switch (@typeInfo(ptr_info.child)) {
+ .Array => |info| {
+ ptr_info.child = info.child;
+ ptr_info.sentinel = info.sentinel;
+ },
+ else => @compileError("invalid type given to std.mem.Span"),
+ },
+ .C => {
+ ptr_info.sentinel = 0;
+ ptr_info.is_allowzero = false;
+ },
+ .Many, .Slice => {},
+ }
+ ptr_info.size = .Slice;
+ return @Type(std.builtin.TypeInfo{ .Pointer = ptr_info });
+}
+
+test "Span" {
+ testing.expect(Span(*[5]u16) == []u16);
+ testing.expect(Span(*const [5]u16) == []const u16);
+ testing.expect(Span([]u16) == []u16);
+ testing.expect(Span([]const u8) == []const u8);
+ testing.expect(Span([:1]u16) == [:1]u16);
+ testing.expect(Span([:1]const u8) == [:1]const u8);
+ testing.expect(Span([*:1]u16) == [:1]u16);
+ testing.expect(Span([*:1]const u8) == [:1]const u8);
+ testing.expect(Span([*c]u16) == [:0]u16);
+ testing.expect(Span([*c]const u8) == [:0]const u8);
+}
+
+/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and
+/// returns a slice. If there is a sentinel on the input type, there will be a
+/// sentinel on the output type. The constness of the output type matches
+/// the constness of the input type.
+pub fn span(ptr: var) Span(@TypeOf(ptr)) {
+ const Result = Span(@TypeOf(ptr));
+ const l = len(ptr);
+ if (@typeInfo(Result).Pointer.sentinel) |s| {
+ return ptr[0..l :s];
+ } else {
+ return ptr[0..l];
+ }
+}
+
+test "span" {
+ var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
+ const ptr = array[0..2 :3].ptr;
+ testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 }));
+ testing.expect(eql(u16, span(&array), &[_]u16{ 1, 2, 3, 4, 5 }));
+}
+
+/// Takes a pointer to an array, an array, a sentinel-terminated pointer,
+/// or a slice, and returns the length.
+pub fn len(ptr: var) usize {
+ return switch (@typeInfo(@TypeOf(ptr))) {
+ .Array => |info| info.len,
+ .Pointer => |info| switch (info.size) {
+ .One => switch (@typeInfo(info.child)) {
+ .Array => |x| x.len,
+ else => @compileError("invalid type given to std.mem.length"),
+ },
+ .Many => if (info.sentinel) |sentinel|
+ indexOfSentinel(info.child, sentinel, ptr)
+ else
+ @compileError("length of pointer with no sentinel"),
+ .C => indexOfSentinel(info.child, 0, ptr),
+ .Slice => ptr.len,
+ },
+ else => @compileError("invalid type given to std.mem.length"),
+ };
+}
+
+test "len" {
+ testing.expect(len("aoeu") == 4);
+
+ {
+ var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
+ testing.expect(len(&array) == 5);
+ testing.expect(len(array[0..3]) == 3);
+ array[2] = 0;
+ const ptr = array[0..2 :0].ptr;
+ testing.expect(len(ptr) == 2);
+ }
+}
+
+pub fn indexOfSentinel(comptime Elem: type, comptime sentinel: Elem, ptr: [*:sentinel]const Elem) usize {
+ var i: usize = 0;
+ while (ptr[i] != sentinel) {
+ i += 1;
+ }
+ return i;
}
/// Returns true if all elements in a slice are equal to the scalar value provided
@@ -637,12 +746,12 @@ test "mem.indexOf" {
pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: builtin.Endian) ReturnType {
var result: ReturnType = 0;
switch (endian) {
- builtin.Endian.Big => {
+ .Big => {
for (bytes) |b| {
result = (result << 8) | b;
}
},
- builtin.Endian.Little => {
+ .Little => {
const ShiftType = math.Log2Int(ReturnType);
for (bytes) |b, index| {
result = result | (@as(ReturnType, b) << @intCast(ShiftType, index * 8));
@@ -670,13 +779,13 @@ pub fn readIntForeign(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)
}
pub const readIntLittle = switch (builtin.endian) {
- builtin.Endian.Little => readIntNative,
- builtin.Endian.Big => readIntForeign,
+ .Little => readIntNative,
+ .Big => readIntForeign,
};
pub const readIntBig = switch (builtin.endian) {
- builtin.Endian.Little => readIntForeign,
- builtin.Endian.Big => readIntNative,
+ .Little => readIntForeign,
+ .Big => readIntNative,
};
/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0
@@ -700,13 +809,13 @@ pub fn readIntSliceForeign(comptime T: type, bytes: []const u8) T {
}
pub const readIntSliceLittle = switch (builtin.endian) {
- builtin.Endian.Little => readIntSliceNative,
- builtin.Endian.Big => readIntSliceForeign,
+ .Little => readIntSliceNative,
+ .Big => readIntSliceForeign,
};
pub const readIntSliceBig = switch (builtin.endian) {
- builtin.Endian.Little => readIntSliceForeign,
- builtin.Endian.Big => readIntSliceNative,
+ .Little => readIntSliceForeign,
+ .Big => readIntSliceNative,
};
/// Reads an integer from memory with bit count specified by T.
@@ -783,13 +892,13 @@ pub fn writeIntForeign(comptime T: type, buf: *[@divExact(T.bit_count, 8)]u8, va
}
pub const writeIntLittle = switch (builtin.endian) {
- builtin.Endian.Little => writeIntNative,
- builtin.Endian.Big => writeIntForeign,
+ .Little => writeIntNative,
+ .Big => writeIntForeign,
};
pub const writeIntBig = switch (builtin.endian) {
- builtin.Endian.Little => writeIntForeign,
- builtin.Endian.Big => writeIntNative,
+ .Little => writeIntForeign,
+ .Big => writeIntNative,
};
/// Writes an integer to memory, storing it in twos-complement.
@@ -841,13 +950,13 @@ pub fn writeIntSliceBig(comptime T: type, buffer: []u8, value: T) void {
}
pub const writeIntSliceNative = switch (builtin.endian) {
- builtin.Endian.Little => writeIntSliceLittle,
- builtin.Endian.Big => writeIntSliceBig,
+ .Little => writeIntSliceLittle,
+ .Big => writeIntSliceBig,
};
pub const writeIntSliceForeign = switch (builtin.endian) {
- builtin.Endian.Little => writeIntSliceBig,
- builtin.Endian.Big => writeIntSliceLittle,
+ .Little => writeIntSliceBig,
+ .Big => writeIntSliceLittle,
};
/// Writes a twos-complement integer to memory, with the specified endianness.
@@ -858,10 +967,10 @@ pub const writeIntSliceForeign = switch (builtin.endian) {
/// use writeInt instead.
pub fn writeIntSlice(comptime T: type, buffer: []u8, value: T, endian: builtin.Endian) void {
comptime assert(T.bit_count % 8 == 0);
- switch (endian) {
- builtin.Endian.Little => return writeIntSliceLittle(T, buffer, value),
- builtin.Endian.Big => return writeIntSliceBig(T, buffer, value),
- }
+ return switch (endian) {
+ .Little => writeIntSliceLittle(T, buffer, value),
+ .Big => writeIntSliceBig(T, buffer, value),
+ };
}
test "writeIntBig and writeIntLittle" {
@@ -1397,54 +1506,54 @@ test "rotate" {
/// Converts a little-endian integer to host endianness.
pub fn littleToNative(comptime T: type, x: T) T {
return switch (builtin.endian) {
- builtin.Endian.Little => x,
- builtin.Endian.Big => @byteSwap(T, x),
+ .Little => x,
+ .Big => @byteSwap(T, x),
};
}
/// Converts a big-endian integer to host endianness.
pub fn bigToNative(comptime T: type, x: T) T {
return switch (builtin.endian) {
- builtin.Endian.Little => @byteSwap(T, x),
- builtin.Endian.Big => x,
+ .Little => @byteSwap(T, x),
+ .Big => x,
};
}
/// Converts an integer from specified endianness to host endianness.
pub fn toNative(comptime T: type, x: T, endianness_of_x: builtin.Endian) T {
return switch (endianness_of_x) {
- builtin.Endian.Little => littleToNative(T, x),
- builtin.Endian.Big => bigToNative(T, x),
+ .Little => littleToNative(T, x),
+ .Big => bigToNative(T, x),
};
}
/// Converts an integer which has host endianness to the desired endianness.
pub fn nativeTo(comptime T: type, x: T, desired_endianness: builtin.Endian) T {
return switch (desired_endianness) {
- builtin.Endian.Little => nativeToLittle(T, x),
- builtin.Endian.Big => nativeToBig(T, x),
+ .Little => nativeToLittle(T, x),
+ .Big => nativeToBig(T, x),
};
}
/// Converts an integer which has host endianness to little endian.
pub fn nativeToLittle(comptime T: type, x: T) T {
return switch (builtin.endian) {
- builtin.Endian.Little => x,
- builtin.Endian.Big => @byteSwap(T, x),
+ .Little => x,
+ .Big => @byteSwap(T, x),
};
}
/// Converts an integer which has host endianness to big endian.
pub fn nativeToBig(comptime T: type, x: T) T {
return switch (builtin.endian) {
- builtin.Endian.Little => @byteSwap(T, x),
- builtin.Endian.Big => x,
+ .Little => @byteSwap(T, x),
+ .Big => x,
};
}
fn AsBytesReturnType(comptime P: type) type {
if (comptime !trait.isSingleItemPtr(P))
- @compileError("expected single item " ++ "pointer, passed " ++ @typeName(P));
+ @compileError("expected single item pointer, passed " ++ @typeName(P));
const size = @as(usize, @sizeOf(meta.Child(P)));
const alignment = comptime meta.alignment(P);
@@ -1469,8 +1578,8 @@ pub fn asBytes(ptr: var) AsBytesReturnType(@TypeOf(ptr)) {
test "asBytes" {
const deadbeef = @as(u32, 0xDEADBEEF);
const deadbeef_bytes = switch (builtin.endian) {
- builtin.Endian.Big => "\xDE\xAD\xBE\xEF",
- builtin.Endian.Little => "\xEF\xBE\xAD\xDE",
+ .Big => "\xDE\xAD\xBE\xEF",
+ .Little => "\xEF\xBE\xAD\xDE",
};
testing.expect(eql(u8, asBytes(&deadbeef), deadbeef_bytes));
@@ -1508,21 +1617,21 @@ pub fn toBytes(value: var) [@sizeOf(@TypeOf(value))]u8 {
test "toBytes" {
var my_bytes = toBytes(@as(u32, 0x12345678));
switch (builtin.endian) {
- builtin.Endian.Big => testing.expect(eql(u8, &my_bytes, "\x12\x34\x56\x78")),
- builtin.Endian.Little => testing.expect(eql(u8, &my_bytes, "\x78\x56\x34\x12")),
+ .Big => testing.expect(eql(u8, &my_bytes, "\x12\x34\x56\x78")),
+ .Little => testing.expect(eql(u8, &my_bytes, "\x78\x56\x34\x12")),
}
my_bytes[0] = '\x99';
switch (builtin.endian) {
- builtin.Endian.Big => testing.expect(eql(u8, &my_bytes, "\x99\x34\x56\x78")),
- builtin.Endian.Little => testing.expect(eql(u8, &my_bytes, "\x99\x56\x34\x12")),
+ .Big => testing.expect(eql(u8, &my_bytes, "\x99\x34\x56\x78")),
+ .Little => testing.expect(eql(u8, &my_bytes, "\x99\x56\x34\x12")),
}
}
fn BytesAsValueReturnType(comptime T: type, comptime B: type) type {
const size = @as(usize, @sizeOf(T));
- if (comptime !trait.is(builtin.TypeId.Pointer)(B) or
+ if (comptime !trait.is(.Pointer)(B) or
(meta.Child(B) != [size]u8 and meta.Child(B) != [size:0]u8))
{
@compileError("expected *[N]u8 " ++ ", passed " ++ @typeName(B));
@@ -1542,15 +1651,15 @@ pub fn bytesAsValue(comptime T: type, bytes: var) BytesAsValueReturnType(T, @Typ
test "bytesAsValue" {
const deadbeef = @as(u32, 0xDEADBEEF);
const deadbeef_bytes = switch (builtin.endian) {
- builtin.Endian.Big => "\xDE\xAD\xBE\xEF",
- builtin.Endian.Little => "\xEF\xBE\xAD\xDE",
+ .Big => "\xDE\xAD\xBE\xEF",
+ .Little => "\xEF\xBE\xAD\xDE",
};
testing.expect(deadbeef == bytesAsValue(u32, deadbeef_bytes).*);
var codeface_bytes: [4]u8 = switch (builtin.endian) {
- builtin.Endian.Big => "\xC0\xDE\xFA\xCE",
- builtin.Endian.Little => "\xCE\xFA\xDE\xC0",
+ .Big => "\xC0\xDE\xFA\xCE",
+ .Little => "\xCE\xFA\xDE\xC0",
}.*;
var codeface = bytesAsValue(u32, &codeface_bytes);
testing.expect(codeface.* == 0xC0DEFACE);
@@ -1583,8 +1692,8 @@ pub fn bytesToValue(comptime T: type, bytes: var) T {
}
test "bytesToValue" {
const deadbeef_bytes = switch (builtin.endian) {
- builtin.Endian.Big => "\xDE\xAD\xBE\xEF",
- builtin.Endian.Little => "\xEF\xBE\xAD\xDE",
+ .Big => "\xDE\xAD\xBE\xEF",
+ .Little => "\xEF\xBE\xAD\xDE",
};
const deadbeef = bytesToValue(u32, deadbeef_bytes);
@@ -1753,8 +1862,13 @@ fn SubArrayPtrReturnType(comptime T: type, comptime length: usize) type {
return *[length]meta.Child(meta.Child(T));
}
-///Given a pointer to an array, returns a pointer to a portion of that array, preserving constness.
-pub fn subArrayPtr(ptr: var, comptime start: usize, comptime length: usize) SubArrayPtrReturnType(@TypeOf(ptr), length) {
+/// Given a pointer to an array, returns a pointer to a portion of that array, preserving constness.
+/// TODO this will be obsoleted by https://github.com/ziglang/zig/issues/863
+pub fn subArrayPtr(
+ ptr: var,
+ comptime start: usize,
+ comptime length: usize,
+) SubArrayPtrReturnType(@TypeOf(ptr), length) {
assert(start + length <= ptr.*.len);
const ReturnType = SubArrayPtrReturnType(@TypeOf(ptr), length);
diff --git a/lib/std/meta.zig b/lib/std/meta.zig
index 58fd6b9da7..65809abb5c 100644
--- a/lib/std/meta.zig
+++ b/lib/std/meta.zig
@@ -115,6 +115,32 @@ test "std.meta.Child" {
testing.expect(Child(?u8) == u8);
}
+/// Given a type with a sentinel e.g. `[:0]u8`, returns the sentinel
+pub fn Sentinel(comptime T: type) Child(T) {
+ // comptime asserts that ptr has a sentinel
+ switch (@typeInfo(T)) {
+ .Array => |arrayInfo| {
+ return comptime arrayInfo.sentinel.?;
+ },
+ .Pointer => |ptrInfo| {
+ switch (ptrInfo.size) {
+ .Many, .Slice => {
+ return comptime ptrInfo.sentinel.?;
+ },
+ else => {},
+ }
+ },
+ else => {},
+ }
+ @compileError("not a sentinel type, found '" ++ @typeName(T) ++ "'");
+}
+
+test "std.meta.Sentinel" {
+ testing.expectEqual(@as(u8, 0), Sentinel([:0]u8));
+ testing.expectEqual(@as(u8, 0), Sentinel([*:0]u8));
+ testing.expectEqual(@as(u8, 0), Sentinel([5:0]u8));
+}
+
pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout {
return switch (@typeInfo(T)) {
.Struct => |info| info.layout,
diff --git a/lib/std/mutex.zig b/lib/std/mutex.zig
index 6954b2fb17..a57519cd14 100644
--- a/lib/std/mutex.zig
+++ b/lib/std/mutex.zig
@@ -73,7 +73,7 @@ pub const Mutex = if (builtin.single_threaded)
return self.tryAcquire() orelse @panic("deadlock detected");
}
}
-else if (builtin.os == .windows)
+else if (builtin.os.tag == .windows)
// https://locklessinc.com/articles/keyed_events/
extern union {
locked: u8,
@@ -161,7 +161,7 @@ else if (builtin.os == .windows)
}
};
}
-else if (builtin.link_libc or builtin.os == .linux)
+else if (builtin.link_libc or builtin.os.tag == .linux)
// stack-based version of https://github.com/Amanieu/parking_lot/blob/master/core/src/word_lock.rs
struct {
state: usize,
diff --git a/lib/std/net.zig b/lib/std/net.zig
index 898ba086be..6d0daefdc0 100644
--- a/lib/std/net.zig
+++ b/lib/std/net.zig
@@ -352,7 +352,7 @@ pub const Address = extern union {
unreachable;
}
- const path_len = std.mem.len(u8, @ptrCast([*:0]const u8, &self.un.path));
+ const path_len = std.mem.len(@ptrCast([*:0]const u8, &self.un.path));
return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len);
},
else => unreachable,
@@ -501,7 +501,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
return result;
}
- if (builtin.os == .linux) {
+ if (builtin.os.tag == .linux) {
const flags = std.c.AI_NUMERICSERV;
const family = os.AF_UNSPEC;
var lookup_addrs = std.ArrayList(LookupAddr).init(allocator);
diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig
index 45d8b1cffd..4f3d955f30 100644
--- a/lib/std/net/test.zig
+++ b/lib/std/net/test.zig
@@ -63,7 +63,7 @@ test "parse and render IPv4 addresses" {
}
test "resolve DNS" {
- if (std.builtin.os == .windows) {
+ if (std.builtin.os.tag == .windows) {
// DNS resolution not implemented on Windows yet.
return error.SkipZigTest;
}
@@ -81,7 +81,7 @@ test "resolve DNS" {
test "listen on a port, send bytes, receive bytes" {
if (!std.io.is_async) return error.SkipZigTest;
- if (std.builtin.os != .linux) {
+ if (std.builtin.os.tag != .linux) {
// TODO build abstractions for other operating systems
return error.SkipZigTest;
}
diff --git a/lib/std/os.zig b/lib/std/os.zig
index 3b60a08cef..fbfef4ac4c 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -56,7 +56,7 @@ pub const system = if (@hasDecl(root, "os") and root.os != @This())
root.os.system
else if (builtin.link_libc)
std.c
-else switch (builtin.os) {
+else switch (builtin.os.tag) {
.macosx, .ios, .watchos, .tvos => darwin,
.freebsd => freebsd,
.linux => linux,
@@ -93,10 +93,10 @@ pub const errno = system.getErrno;
/// must call `fsync` before `close`.
/// Note: The Zig standard library does not support POSIX thread cancellation.
pub fn close(fd: fd_t) void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return windows.CloseHandle(fd);
}
- if (builtin.os == .wasi) {
+ if (builtin.os.tag == .wasi) {
_ = wasi.fd_close(fd);
}
if (comptime std.Target.current.isDarwin()) {
@@ -121,12 +121,12 @@ pub const GetRandomError = OpenError;
/// appropriate OS-specific library call. Otherwise it uses the zig standard
/// library implementation.
pub fn getrandom(buffer: []u8) GetRandomError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return windows.RtlGenRandom(buffer);
}
- if (builtin.os == .linux or builtin.os == .freebsd) {
+ if (builtin.os.tag == .linux or builtin.os.tag == .freebsd) {
var buf = buffer;
- const use_c = builtin.os != .linux or
+ const use_c = builtin.os.tag != .linux or
std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok;
while (buf.len != 0) {
@@ -153,7 +153,7 @@ pub fn getrandom(buffer: []u8) GetRandomError!void {
}
return;
}
- if (builtin.os == .wasi) {
+ if (builtin.os.tag == .wasi) {
switch (wasi.random_get(buffer.ptr, buffer.len)) {
0 => return,
else => |err| return unexpectedErrno(err),
@@ -188,13 +188,13 @@ pub fn abort() noreturn {
// MSVCRT abort() sometimes opens a popup window which is undesirable, so
// even when linking libc on Windows we use our own abort implementation.
// See https://github.com/ziglang/zig/issues/2071 for more details.
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
if (builtin.mode == .Debug) {
@breakpoint();
}
windows.kernel32.ExitProcess(3);
}
- if (!builtin.link_libc and builtin.os == .linux) {
+ if (!builtin.link_libc and builtin.os.tag == .linux) {
raise(SIGABRT) catch {};
// TODO the rest of the implementation of abort() from musl libc here
@@ -202,10 +202,10 @@ pub fn abort() noreturn {
raise(SIGKILL) catch {};
exit(127);
}
- if (builtin.os == .uefi) {
+ if (builtin.os.tag == .uefi) {
exit(0); // TODO choose appropriate exit code
}
- if (builtin.os == .wasi) {
+ if (builtin.os.tag == .wasi) {
@breakpoint();
exit(1);
}
@@ -223,7 +223,7 @@ pub fn raise(sig: u8) RaiseError!void {
}
}
- if (builtin.os == .linux) {
+ if (builtin.os.tag == .linux) {
var set: linux.sigset_t = undefined;
// block application signals
_ = linux.sigprocmask(SIG_BLOCK, &linux.app_mask, &set);
@@ -260,16 +260,16 @@ pub fn exit(status: u8) noreturn {
if (builtin.link_libc) {
system.exit(status);
}
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
windows.kernel32.ExitProcess(status);
}
- if (builtin.os == .wasi) {
+ if (builtin.os.tag == .wasi) {
wasi.proc_exit(status);
}
- if (builtin.os == .linux and !builtin.single_threaded) {
+ if (builtin.os.tag == .linux and !builtin.single_threaded) {
linux.exit_group(status);
}
- if (builtin.os == .uefi) {
+ if (builtin.os.tag == .uefi) {
// exit() is only avaliable if exitBootServices() has not been called yet.
// This call to exit should not fail, so we don't care about its return value.
if (uefi.system_table.boot_services) |bs| {
@@ -299,11 +299,11 @@ pub const ReadError = error{
/// If the application has a global event loop enabled, EAGAIN is handled
/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return windows.ReadFile(fd, buf, null);
}
- if (builtin.os == .wasi and !builtin.link_libc) {
+ if (builtin.os.tag == .wasi and !builtin.link_libc) {
const iovs = [1]iovec{iovec{
.iov_base = buf.ptr,
.iov_len = buf.len,
@@ -352,7 +352,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
/// * Windows
/// On these systems, the read races with concurrent writes to the same file descriptor.
pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
// TODO batch these into parallel requests
var off: usize = 0;
var iov_i: usize = 0;
@@ -406,7 +406,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
pub fn pread(fd: fd_t, buf: []u8, offset: u64) ReadError!usize {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return windows.ReadFile(fd, buf, offset);
}
@@ -493,7 +493,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize {
}
}
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
// TODO batch these into parallel requests
var off: usize = 0;
var iov_i: usize = 0;
@@ -557,11 +557,11 @@ pub const WriteError = error{
/// If the application has a global event loop enabled, EAGAIN is handled
/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return windows.WriteFile(fd, bytes, null);
}
- if (builtin.os == .wasi and !builtin.link_libc) {
+ if (builtin.os.tag == .wasi and !builtin.link_libc) {
const ciovs = [1]iovec_const{iovec_const{
.iov_base = bytes.ptr,
.iov_len = bytes.len,
@@ -650,7 +650,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void {
/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) WriteError!void {
- if (comptime std.Target.current.isWindows()) {
+ if (std.Target.current.os.tag == .windows) {
return windows.WriteFile(fd, bytes, offset);
}
@@ -739,7 +739,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void
}
}
- if (comptime std.Target.current.isWindows()) {
+ if (std.Target.current.os.tag == .windows) {
var off = offset;
for (iov) |item| {
try pwrite(fd, item.iov_base[0..item.iov_len], off);
@@ -1095,7 +1095,7 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.
pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) void {
for (envp_buf) |env| {
- const env_buf = if (env) |ptr| ptr[0 .. mem.len(u8, ptr) + 1] else break;
+ const env_buf = if (env) |ptr| ptr[0 .. mem.len(ptr) + 1] else break;
allocator.free(env_buf);
}
allocator.free(envp_buf);
@@ -1129,7 +1129,7 @@ pub fn getenv(key: []const u8) ?[]const u8 {
}
return null;
}
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
@compileError("std.os.getenv is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API.");
}
// TODO see https://github.com/ziglang/zig/issues/4524
@@ -1158,7 +1158,7 @@ pub fn getenvZ(key: [*:0]const u8) ?[]const u8 {
const value = system.getenv(key) orelse return null;
return mem.toSliceConst(u8, value);
}
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
@compileError("std.os.getenvZ is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API.");
}
return getenv(mem.toSliceConst(u8, key));
@@ -1167,7 +1167,7 @@ pub fn getenvZ(key: [*:0]const u8) ?[]const u8 {
/// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name.
/// See also `getenv`.
pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 {
- if (builtin.os != .windows) {
+ if (builtin.os.tag != .windows) {
@compileError("std.os.getenvW is a Windows-only API");
}
const key_slice = mem.toSliceConst(u16, key);
@@ -1199,7 +1199,7 @@ pub const GetCwdError = error{
/// The result is a slice of out_buffer, indexed from 0.
pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return windows.GetCurrentDirectory(out_buffer);
}
@@ -1240,7 +1240,7 @@ pub const SymLinkError = error{
/// If `sym_link_path` exists, it will not be overwritten.
/// See also `symlinkC` and `symlinkW`.
pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const target_path_w = try windows.sliceToPrefixedFileW(target_path);
const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0);
@@ -1254,7 +1254,7 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!
/// This is the same as `symlink` except the parameters are null-terminated pointers.
/// See also `symlink`.
pub fn symlinkC(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const target_path_w = try windows.cStrToPrefixedFileW(target_path);
const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path);
return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0);
@@ -1329,7 +1329,7 @@ pub const UnlinkError = error{
/// Delete a name and possibly the file it refers to.
/// See also `unlinkC`.
pub fn unlink(file_path: []const u8) UnlinkError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
return windows.DeleteFileW(&file_path_w);
} else {
@@ -1340,7 +1340,7 @@ pub fn unlink(file_path: []const u8) UnlinkError!void {
/// Same as `unlink` except the parameter is a null terminated UTF8-encoded string.
pub fn unlinkC(file_path: [*:0]const u8) UnlinkError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
return windows.DeleteFileW(&file_path_w);
}
@@ -1372,7 +1372,7 @@ pub const UnlinkatError = UnlinkError || error{
/// Asserts that the path parameter has no null bytes.
pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
if (std.debug.runtime_safety) for (file_path) |byte| assert(byte != 0);
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
return unlinkatW(dirfd, &file_path_w, flags);
}
@@ -1382,7 +1382,7 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo
/// Same as `unlinkat` but `file_path` is a null-terminated string.
pub fn unlinkatC(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);
return unlinkatW(dirfd, &file_path_w, flags);
}
@@ -1438,7 +1438,7 @@ pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatEr
var attr = w.OBJECT_ATTRIBUTES{
.Length = @sizeOf(w.OBJECT_ATTRIBUTES),
- .RootDirectory = dirfd,
+ .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd,
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
.ObjectName = &nt_name,
.SecurityDescriptor = null,
@@ -1493,7 +1493,7 @@ const RenameError = error{
/// Change the name or location of a file.
pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const old_path_w = try windows.sliceToPrefixedFileW(old_path);
const new_path_w = try windows.sliceToPrefixedFileW(new_path);
return renameW(&old_path_w, &new_path_w);
@@ -1506,7 +1506,7 @@ pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
/// Same as `rename` except the parameters are null-terminated byte arrays.
pub fn renameC(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const old_path_w = try windows.cStrToPrefixedFileW(old_path);
const new_path_w = try windows.cStrToPrefixedFileW(new_path);
return renameW(&old_path_w, &new_path_w);
@@ -1561,7 +1561,7 @@ pub const MakeDirError = error{
/// Create a directory.
/// `mode` is ignored on Windows.
pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
return windows.CreateDirectoryW(&dir_path_w, null);
} else {
@@ -1572,7 +1572,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
/// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string.
pub fn mkdirC(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
return windows.CreateDirectoryW(&dir_path_w, null);
}
@@ -1611,7 +1611,7 @@ pub const DeleteDirError = error{
/// Deletes an empty directory.
pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
return windows.RemoveDirectoryW(&dir_path_w);
} else {
@@ -1622,7 +1622,7 @@ pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
/// Same as `rmdir` except the parameter is null-terminated.
pub fn rmdirC(dir_path: [*:0]const u8) DeleteDirError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
return windows.RemoveDirectoryW(&dir_path_w);
}
@@ -1658,7 +1658,7 @@ pub const ChangeCurDirError = error{
/// Changes the current working directory of the calling process.
/// `dir_path` is recommended to be a UTF-8 encoded string.
pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
@compileError("TODO implement chdir for Windows");
} else {
@@ -1669,7 +1669,7 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
/// Same as `chdir` except the parameter is null-terminated.
pub fn chdirC(dir_path: [*:0]const u8) ChangeCurDirError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
@compileError("TODO implement chdir for Windows");
}
@@ -1700,7 +1700,7 @@ pub const ReadLinkError = error{
/// Read value of a symbolic link.
/// The return value is a slice of `out_buffer` from index 0.
pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
@compileError("TODO implement readlink for Windows");
} else {
@@ -1711,7 +1711,7 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
/// Same as `readlink` except `file_path` is null-terminated.
pub fn readlinkC(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
@compileError("TODO implement readlink for Windows");
}
@@ -1732,7 +1732,7 @@ pub fn readlinkC(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8
}
pub fn readlinkatC(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
@compileError("TODO implement readlink for Windows");
}
@@ -1800,7 +1800,7 @@ pub fn setregid(rgid: u32, egid: u32) SetIdError!void {
/// Test whether a file descriptor refers to a terminal.
pub fn isatty(handle: fd_t) bool {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
if (isCygwinPty(handle))
return true;
@@ -1810,7 +1810,7 @@ pub fn isatty(handle: fd_t) bool {
if (builtin.link_libc) {
return system.isatty(handle) != 0;
}
- if (builtin.os == .wasi) {
+ if (builtin.os.tag == .wasi) {
var statbuf: fdstat_t = undefined;
const err = system.fd_fdstat_get(handle, &statbuf);
if (err != 0) {
@@ -1828,7 +1828,7 @@ pub fn isatty(handle: fd_t) bool {
return true;
}
- if (builtin.os == .linux) {
+ if (builtin.os.tag == .linux) {
var wsz: linux.winsize = undefined;
return linux.syscall3(linux.SYS_ioctl, @bitCast(usize, @as(isize, handle)), linux.TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
}
@@ -1836,7 +1836,7 @@ pub fn isatty(handle: fd_t) bool {
}
pub fn isCygwinPty(handle: fd_t) bool {
- if (builtin.os != .windows) return false;
+ if (builtin.os.tag != .windows) return false;
const size = @sizeOf(windows.FILE_NAME_INFO);
var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (size + windows.MAX_PATH);
@@ -2589,7 +2589,7 @@ pub const AccessError = error{
/// check user's permissions for a file
/// TODO currently this assumes `mode` is `F_OK` on Windows.
pub fn access(path: []const u8, mode: u32) AccessError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const path_w = try windows.sliceToPrefixedFileW(path);
_ = try windows.GetFileAttributesW(&path_w);
return;
@@ -2603,7 +2603,7 @@ pub const accessC = accessZ;
/// Same as `access` except `path` is null-terminated.
pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const path_w = try windows.cStrToPrefixedFileW(path);
_ = try windows.GetFileAttributesW(&path_w);
return;
@@ -2644,7 +2644,7 @@ pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!v
/// Check user's permissions for a file, based on an open directory handle.
/// TODO currently this ignores `mode` and `flags` on Windows.
pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const path_w = try windows.sliceToPrefixedFileW(path);
return faccessatW(dirfd, &path_w, mode, flags);
}
@@ -2654,7 +2654,7 @@ pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessErr
/// Same as `faccessat` except the path parameter is null-terminated.
pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const path_w = try windows.cStrToPrefixedFileW(path);
return faccessatW(dirfd, &path_w, mode, flags);
}
@@ -2764,6 +2764,7 @@ pub const SysCtlError = error{
PermissionDenied,
SystemResources,
NameTooLong,
+ UnknownName,
} || UnexpectedError;
pub fn sysctl(
@@ -2779,6 +2780,7 @@ pub fn sysctl(
EFAULT => unreachable,
EPERM => return error.PermissionDenied,
ENOMEM => return error.SystemResources,
+ ENOENT => return error.UnknownName,
else => |err| return unexpectedErrno(err),
}
}
@@ -2795,6 +2797,7 @@ pub fn sysctlbynameC(
EFAULT => unreachable,
EPERM => return error.PermissionDenied,
ENOMEM => return error.SystemResources,
+ ENOENT => return error.UnknownName,
else => |err| return unexpectedErrno(err),
}
}
@@ -2811,7 +2814,7 @@ pub const SeekError = error{Unseekable} || UnexpectedError;
/// Repositions read/write file offset relative to the beginning.
pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
- if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
+ if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
var result: u64 = undefined;
switch (errno(system.llseek(fd, offset, &result, SEEK_SET))) {
0 => return,
@@ -2823,7 +2826,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
else => |err| return unexpectedErrno(err),
}
}
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return windows.SetFilePointerEx_BEGIN(fd, offset);
}
const ipos = @bitCast(i64, offset); // the OS treats this as unsigned
@@ -2840,7 +2843,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
/// Repositions read/write file offset relative to the current offset.
pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
- if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
+ if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
var result: u64 = undefined;
switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_CUR))) {
0 => return,
@@ -2852,7 +2855,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
else => |err| return unexpectedErrno(err),
}
}
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return windows.SetFilePointerEx_CURRENT(fd, offset);
}
switch (errno(system.lseek(fd, offset, SEEK_CUR))) {
@@ -2868,7 +2871,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
/// Repositions read/write file offset relative to the end.
pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
- if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
+ if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
var result: u64 = undefined;
switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_END))) {
0 => return,
@@ -2880,7 +2883,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
else => |err| return unexpectedErrno(err),
}
}
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return windows.SetFilePointerEx_END(fd, offset);
}
switch (errno(system.lseek(fd, offset, SEEK_END))) {
@@ -2896,7 +2899,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
/// Returns the read/write file offset relative to the beginning.
pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
- if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
+ if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
var result: u64 = undefined;
switch (errno(system.llseek(fd, 0, &result, SEEK_CUR))) {
0 => return result,
@@ -2908,7 +2911,7 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
else => |err| return unexpectedErrno(err),
}
}
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return windows.SetFilePointerEx_CURRENT_get(fd);
}
const rc = system.lseek(fd, 0, SEEK_CUR);
@@ -2957,7 +2960,7 @@ pub const RealPathError = error{
/// The return value is a slice of `out_buffer`, but not necessarily from the beginning.
/// See also `realpathC` and `realpathW`.
pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const pathname_w = try windows.sliceToPrefixedFileW(pathname);
return realpathW(&pathname_w, out_buffer);
}
@@ -2967,11 +2970,11 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE
/// Same as `realpath` except `pathname` is null-terminated.
pub fn realpathC(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const pathname_w = try windows.cStrToPrefixedFileW(pathname);
return realpathW(&pathname_w, out_buffer);
}
- if (builtin.os == .linux and !builtin.link_libc) {
+ if (builtin.os.tag == .linux and !builtin.link_libc) {
const fd = try openC(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0);
defer close(fd);
@@ -3121,7 +3124,7 @@ pub fn dl_iterate_phdr(
pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;
pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
- if (comptime std.Target.current.getOs() == .wasi) {
+ if (std.Target.current.os.tag == .wasi) {
var ts: timestamp_t = undefined;
switch (system.clock_time_get(@bitCast(u32, clk_id), 1, &ts)) {
0 => {
@@ -3144,7 +3147,7 @@ pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
}
pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void {
- if (comptime std.Target.current.getOs() == .wasi) {
+ if (std.Target.current.os.tag == .wasi) {
var ts: timestamp_t = undefined;
switch (system.clock_res_get(@bitCast(u32, clk_id), &ts)) {
0 => res.* = .{
@@ -3222,7 +3225,7 @@ pub const SigaltstackError = error{
} || UnexpectedError;
pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
- if (builtin.os == .windows or builtin.os == .uefi or builtin.os == .wasi)
+ if (builtin.os.tag == .windows or builtin.os.tag == .uefi or builtin.os.tag == .wasi)
@compileError("std.os.sigaltstack not available for this target");
switch (errno(system.sigaltstack(ss, old_ss))) {
@@ -3294,23 +3297,25 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
else => |err| return unexpectedErrno(err),
}
}
- if (builtin.os == .linux) {
- var uts: utsname = undefined;
- switch (errno(system.uname(&uts))) {
- 0 => {
- const hostname = mem.toSlice(u8, @ptrCast([*:0]u8, &uts.nodename));
- mem.copy(u8, name_buffer, hostname);
- return name_buffer[0..hostname.len];
- },
- EFAULT => unreachable,
- EPERM => return error.PermissionDenied,
- else => |err| return unexpectedErrno(err),
- }
+ if (builtin.os.tag == .linux) {
+ const uts = uname();
+ const hostname = mem.toSliceConst(u8, @ptrCast([*:0]const u8, &uts.nodename));
+ mem.copy(u8, name_buffer, hostname);
+ return name_buffer[0..hostname.len];
}
@compileError("TODO implement gethostname for this OS");
}
+pub fn uname() utsname {
+ var uts: utsname = undefined;
+ switch (errno(system.uname(&uts))) {
+ 0 => return uts,
+ EFAULT => unreachable,
+ else => unreachable,
+ }
+}
+
pub fn res_mkquery(
op: u4,
dname: []const u8,
@@ -3611,7 +3616,7 @@ pub const SchedYieldError = error{
};
pub fn sched_yield() SchedYieldError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
// The return value has to do with how many other threads there are; it is not
// an error condition on Windows.
_ = windows.kernel32.SwitchToThread();
diff --git a/lib/std/os/bits.zig b/lib/std/os/bits.zig
index bab9ad0ae5..38f019d775 100644
--- a/lib/std/os/bits.zig
+++ b/lib/std/os/bits.zig
@@ -3,10 +3,10 @@
//! Root source files can define `os.bits` and these will additionally be added
//! to the namespace.
-const builtin = @import("builtin");
+const std = @import("std");
const root = @import("root");
-pub usingnamespace switch (builtin.os) {
+pub usingnamespace switch (std.Target.current.os.tag) {
.macosx, .ios, .tvos, .watchos => @import("bits/darwin.zig"),
.dragonfly => @import("bits/dragonfly.zig"),
.freebsd => @import("bits/freebsd.zig"),
diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig
index d11f206482..30dba85e51 100644
--- a/lib/std/os/linux.zig
+++ b/lib/std/os/linux.zig
@@ -1070,7 +1070,7 @@ pub fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) usi
}
test "" {
- if (builtin.os == .linux) {
+ if (builtin.os.tag == .linux) {
_ = @import("linux/test.zig");
}
}
diff --git a/lib/std/os/linux/vdso.zig b/lib/std/os/linux/vdso.zig
index 868eb26c69..beb98a063d 100644
--- a/lib/std/os/linux/vdso.zig
+++ b/lib/std/os/linux/vdso.zig
@@ -22,7 +22,11 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {
}) {
const this_ph = @intToPtr(*elf.Phdr, ph_addr);
switch (this_ph.p_type) {
- elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr,
+ // On WSL1 as well as older kernels, the VDSO ELF image is pre-linked in the upper half
+ // of the memory space (e.g. p_vaddr = 0xffffffffff700000 on WSL1).
+ // Wrapping operations are used on this line as well as subsequent calculations relative to base
+ // (lines 47, 78) to ensure no overflow check is tripped.
+ elf.PT_LOAD => base = vdso_addr +% this_ph.p_offset -% this_ph.p_vaddr,
elf.PT_DYNAMIC => maybe_dynv = @intToPtr([*]usize, vdso_addr + this_ph.p_offset),
else => {},
}
@@ -40,7 +44,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {
{
var i: usize = 0;
while (dynv[i] != 0) : (i += 2) {
- const p = base + dynv[i + 1];
+ const p = base +% dynv[i + 1];
switch (dynv[i]) {
elf.DT_STRTAB => maybe_strings = @intToPtr([*]u8, p),
elf.DT_SYMTAB => maybe_syms = @intToPtr([*]elf.Sym, p),
@@ -71,7 +75,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {
if (!checkver(maybe_verdef.?, versym[i], vername, strings))
continue;
}
- return base + syms[i].st_value;
+ return base +% syms[i].st_value;
}
return 0;
diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig
index 488a557ed6..197edd82c1 100644
--- a/lib/std/os/test.zig
+++ b/lib/std/os/test.zig
@@ -53,7 +53,7 @@ test "std.Thread.getCurrentId" {
thread.wait();
if (Thread.use_pthreads) {
expect(thread_current_id == thread_id);
- } else if (builtin.os == .windows) {
+ } else if (builtin.os.tag == .windows) {
expect(Thread.getCurrentId() != thread_current_id);
} else {
// If the thread completes very quickly, then thread_id can be 0. See the
@@ -151,7 +151,7 @@ test "realpath" {
}
test "sigaltstack" {
- if (builtin.os == .windows or builtin.os == .wasi) return error.SkipZigTest;
+ if (builtin.os.tag == .windows or builtin.os.tag == .wasi) return error.SkipZigTest;
var st: os.stack_t = undefined;
try os.sigaltstack(null, &st);
@@ -204,7 +204,7 @@ fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void {
}
test "dl_iterate_phdr" {
- if (builtin.os == .windows or builtin.os == .wasi or builtin.os == .macosx)
+ if (builtin.os.tag == .windows or builtin.os.tag == .wasi or builtin.os.tag == .macosx)
return error.SkipZigTest;
var counter: usize = 0;
@@ -213,7 +213,7 @@ test "dl_iterate_phdr" {
}
test "gethostname" {
- if (builtin.os == .windows)
+ if (builtin.os.tag == .windows)
return error.SkipZigTest;
var buf: [os.HOST_NAME_MAX]u8 = undefined;
@@ -222,7 +222,7 @@ test "gethostname" {
}
test "pipe" {
- if (builtin.os == .windows)
+ if (builtin.os.tag == .windows)
return error.SkipZigTest;
var fds = try os.pipe();
@@ -241,7 +241,7 @@ test "argsAlloc" {
test "memfd_create" {
// memfd_create is linux specific.
- if (builtin.os != .linux) return error.SkipZigTest;
+ if (builtin.os.tag != .linux) return error.SkipZigTest;
const fd = std.os.memfd_create("test", 0) catch |err| switch (err) {
// Related: https://github.com/ziglang/zig/issues/4019
error.SystemOutdated => return error.SkipZigTest,
@@ -258,7 +258,7 @@ test "memfd_create" {
}
test "mmap" {
- if (builtin.os == .windows)
+ if (builtin.os.tag == .windows)
return error.SkipZigTest;
// Simple mmap() call with non page-aligned size
@@ -353,7 +353,7 @@ test "mmap" {
}
test "getenv" {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
expect(os.getenvW(&[_:0]u16{ 'B', 'O', 'G', 'U', 'S', 0x11, 0x22, 0x33, 0x44, 0x55 }) == null);
} else {
expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null);
diff --git a/lib/std/os/windows/bits.zig b/lib/std/os/windows/bits.zig
index 4e153d40ed..f9ba6cf40e 100644
--- a/lib/std/os/windows/bits.zig
+++ b/lib/std/os/windows/bits.zig
@@ -23,6 +23,7 @@ pub const BOOL = c_int;
pub const BOOLEAN = BYTE;
pub const BYTE = u8;
pub const CHAR = u8;
+pub const UCHAR = u8;
pub const FLOAT = f32;
pub const HANDLE = *c_void;
pub const HCRYPTPROV = ULONG_PTR;
@@ -54,6 +55,7 @@ pub const WORD = u16;
pub const DWORD = u32;
pub const DWORD64 = u64;
pub const LARGE_INTEGER = i64;
+pub const ULARGE_INTEGER = u64;
pub const USHORT = u16;
pub const SHORT = i16;
pub const ULONG = u32;
@@ -1145,32 +1147,202 @@ pub const UNICODE_STRING = extern struct {
Buffer: [*]WCHAR,
};
+const ACTIVATION_CONTEXT_DATA = @OpaqueType();
+const ASSEMBLY_STORAGE_MAP = @OpaqueType();
+const FLS_CALLBACK_INFO = @OpaqueType();
+const RTL_BITMAP = @OpaqueType();
+pub const PRTL_BITMAP = *RTL_BITMAP;
+const KAFFINITY = usize;
+
+/// Process Environment Block
+/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
+/// - https://github.com/wine-mirror/wine/blob/1aff1e6a370ee8c0213a0fd4b220d121da8527aa/include/winternl.h#L269
+/// - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/index.htm
pub const PEB = extern struct {
- Reserved1: [2]BYTE,
- BeingDebugged: BYTE,
- Reserved2: [1]BYTE,
- Reserved3: [2]PVOID,
+ // Versions: All
+ InheritedAddressSpace: BOOLEAN,
+
+ // Versions: 3.51+
+ ReadImageFileExecOptions: BOOLEAN,
+ BeingDebugged: BOOLEAN,
+
+ // Versions: 5.2+ (previously was padding)
+ BitField: UCHAR,
+
+ // Versions: all
+ Mutant: HANDLE,
+ ImageBaseAddress: HMODULE,
Ldr: *PEB_LDR_DATA,
ProcessParameters: *RTL_USER_PROCESS_PARAMETERS,
- Reserved4: [3]PVOID,
+ SubSystemData: PVOID,
+ ProcessHeap: HANDLE,
+
+ // Versions: 5.1+
+ FastPebLock: *RTL_CRITICAL_SECTION,
+
+ // Versions: 5.2+
AtlThunkSListPtr: PVOID,
- Reserved5: PVOID,
- Reserved6: ULONG,
- Reserved7: PVOID,
- Reserved8: ULONG,
+ IFEOKey: PVOID,
+
+ // Versions: 6.0+
+
+ /// https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/crossprocessflags.htm
+ CrossProcessFlags: ULONG,
+
+ // Versions: 6.0+
+ union1: extern union {
+ KernelCallbackTable: PVOID,
+ UserSharedInfoPtr: PVOID,
+ },
+
+ // Versions: 5.1+
+ SystemReserved: ULONG,
+
+ // Versions: 5.1, (not 5.2, not 6.0), 6.1+
AtlThunkSListPtr32: ULONG,
- Reserved9: [45]PVOID,
- Reserved10: [96]BYTE,
- PostProcessInitRoutine: PPS_POST_PROCESS_INIT_ROUTINE,
- Reserved11: [128]BYTE,
- Reserved12: [1]PVOID,
+
+ // Versions: 6.1+
+ ApiSetMap: PVOID,
+
+ // Versions: all
+ TlsExpansionCounter: ULONG,
+ // note: there is padding here on 64 bit
+ TlsBitmap: PRTL_BITMAP,
+ TlsBitmapBits: [2]ULONG,
+ ReadOnlySharedMemoryBase: PVOID,
+
+ // Versions: 1703+
+ SharedData: PVOID,
+
+ // Versions: all
+ ReadOnlyStaticServerData: *PVOID,
+ AnsiCodePageData: PVOID,
+ OemCodePageData: PVOID,
+ UnicodeCaseTableData: PVOID,
+
+ // Versions: 3.51+
+ NumberOfProcessors: ULONG,
+ NtGlobalFlag: ULONG,
+
+ // Versions: all
+ CriticalSectionTimeout: LARGE_INTEGER,
+
+ // End of Original PEB size
+
+ // Fields appended in 3.51:
+ HeapSegmentReserve: ULONG_PTR,
+ HeapSegmentCommit: ULONG_PTR,
+ HeapDeCommitTotalFreeThreshold: ULONG_PTR,
+ HeapDeCommitFreeBlockThreshold: ULONG_PTR,
+ NumberOfHeaps: ULONG,
+ MaximumNumberOfHeaps: ULONG,
+ ProcessHeaps: *PVOID,
+
+ // Fields appended in 4.0:
+ GdiSharedHandleTable: PVOID,
+ ProcessStarterHelper: PVOID,
+ GdiDCAttributeList: ULONG,
+ // note: there is padding here on 64 bit
+ LoaderLock: *RTL_CRITICAL_SECTION,
+ OSMajorVersion: ULONG,
+ OSMinorVersion: ULONG,
+ OSBuildNumber: USHORT,
+ OSCSDVersion: USHORT,
+ OSPlatformId: ULONG,
+ ImageSubSystem: ULONG,
+ ImageSubSystemMajorVersion: ULONG,
+ ImageSubSystemMinorVersion: ULONG,
+ // note: there is padding here on 64 bit
+ ActiveProcessAffinityMask: KAFFINITY,
+ GdiHandleBuffer: [switch (@sizeOf(usize)) {
+ 4 => 0x22,
+ 8 => 0x3C,
+ else => unreachable,
+ }]ULONG,
+
+ // Fields appended in 5.0 (Windows 2000):
+ PostProcessInitRoutine: PVOID,
+ TlsExpansionBitmap: PRTL_BITMAP,
+ TlsExpansionBitmapBits: [32]ULONG,
SessionId: ULONG,
+ // note: there is padding here on 64 bit
+ // Versions: 5.1+
+ AppCompatFlags: ULARGE_INTEGER,
+ AppCompatFlagsUser: ULARGE_INTEGER,
+ ShimData: PVOID,
+ // Versions: 5.0+
+ AppCompatInfo: PVOID,
+ CSDVersion: UNICODE_STRING,
+
+ // Fields appended in 5.1 (Windows XP):
+ ActivationContextData: *const ACTIVATION_CONTEXT_DATA,
+ ProcessAssemblyStorageMap: *ASSEMBLY_STORAGE_MAP,
+ SystemDefaultActivationData: *const ACTIVATION_CONTEXT_DATA,
+ SystemAssemblyStorageMap: *ASSEMBLY_STORAGE_MAP,
+ MinimumStackCommit: ULONG_PTR,
+
+ // Fields appended in 5.2 (Windows Server 2003):
+ FlsCallback: *FLS_CALLBACK_INFO,
+ FlsListHead: LIST_ENTRY,
+ FlsBitmap: PRTL_BITMAP,
+ FlsBitmapBits: [4]ULONG,
+ FlsHighIndex: ULONG,
+
+ // Fields appended in 6.0 (Windows Vista):
+ WerRegistrationData: PVOID,
+ WerShipAssertPtr: PVOID,
+
+ // Fields appended in 6.1 (Windows 7):
+ pUnused: PVOID, // previously pContextData
+ pImageHeaderHash: PVOID,
+
+ /// TODO: https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/tracingflags.htm
+ TracingFlags: ULONG,
+
+ // Fields appended in 6.2 (Windows 8):
+ CsrServerReadOnlySharedMemoryBase: ULONGLONG,
+
+ // Fields appended in 1511:
+ TppWorkerpListLock: ULONG,
+ TppWorkerpList: LIST_ENTRY,
+ WaitOnAddressHashTable: [0x80]PVOID,
+
+ // Fields appended in 1709:
+ TelemetryCoverageHeader: PVOID,
+ CloudFileFlags: ULONG,
};
+/// The `PEB_LDR_DATA` structure is the main record of what modules are loaded in a process.
+/// It is essentially the head of three double-linked lists of `LDR_DATA_TABLE_ENTRY` structures which each represent one loaded module.
+///
+/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
+/// - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb_ldr_data.htm
pub const PEB_LDR_DATA = extern struct {
- Reserved1: [8]BYTE,
- Reserved2: [3]PVOID,
+ // Versions: 3.51 and higher
+ /// The size in bytes of the structure
+ Length: ULONG,
+
+ /// TRUE if the structure is prepared.
+ Initialized: BOOLEAN,
+
+ SsHandle: PVOID,
+ InLoadOrderModuleList: LIST_ENTRY,
InMemoryOrderModuleList: LIST_ENTRY,
+ InInitializationOrderModuleList: LIST_ENTRY,
+
+ // Versions: 5.1 and higher
+
+ /// No known use of this field is known in Windows 8 and higher.
+ EntryInProgress: PVOID,
+
+ // Versions: 6.0 from Windows Vista SP1, and higher
+ ShutdownInProgress: BOOLEAN,
+
+ /// Though ShutdownThreadId is declared as a HANDLE,
+ /// it is indeed the thread ID as suggested by its name.
+ /// It is picked up from the UniqueThread member of the CLIENT_ID in the
+ /// TEB of the thread that asks to terminate the process.
+ ShutdownThreadId: HANDLE,
};
pub const RTL_USER_PROCESS_PARAMETERS = extern struct {
@@ -1321,3 +1493,16 @@ pub const PSAPI_WS_WATCH_INFORMATION_EX = extern struct {
Flags: ULONG_PTR,
};
pub const PPSAPI_WS_WATCH_INFORMATION_EX = *PSAPI_WS_WATCH_INFORMATION_EX;
+
+pub const OSVERSIONINFOW = extern struct {
+ dwOSVersionInfoSize: ULONG,
+ dwMajorVersion: ULONG,
+ dwMinorVersion: ULONG,
+ dwBuildNumber: ULONG,
+ dwPlatformId: ULONG,
+ szCSDVersion: [128]WCHAR,
+};
+pub const POSVERSIONINFOW = *OSVERSIONINFOW;
+pub const LPOSVERSIONINFOW = *OSVERSIONINFOW;
+pub const RTL_OSVERSIONINFOW = OSVERSIONINFOW;
+pub const PRTL_OSVERSIONINFOW = *RTL_OSVERSIONINFOW;
diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig
index e98a2e6a87..49e60803bc 100644
--- a/lib/std/os/windows/ntdll.zig
+++ b/lib/std/os/windows/ntdll.zig
@@ -1,6 +1,14 @@
usingnamespace @import("bits.zig");
-pub extern "NtDll" fn RtlCaptureStackBackTrace(FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **c_void, BackTraceHash: ?*DWORD) callconv(.Stdcall) WORD;
+pub extern "NtDll" fn RtlGetVersion(
+ lpVersionInformation: PRTL_OSVERSIONINFOW,
+) callconv(.Stdcall) NTSTATUS;
+pub extern "NtDll" fn RtlCaptureStackBackTrace(
+ FramesToSkip: DWORD,
+ FramesToCapture: DWORD,
+ BackTrace: **c_void,
+ BackTraceHash: ?*DWORD,
+) callconv(.Stdcall) WORD;
pub extern "NtDll" fn NtQueryInformationFile(
FileHandle: HANDLE,
IoStatusBlock: *IO_STATUS_BLOCK,
diff --git a/lib/std/packed_int_array.zig b/lib/std/packed_int_array.zig
index 63b6fffa73..51be01315e 100644
--- a/lib/std/packed_int_array.zig
+++ b/lib/std/packed_int_array.zig
@@ -593,7 +593,7 @@ test "PackedInt(Array/Slice)Endian" {
// after this one is not mapped and will cause a segfault if we
// don't account for the bounds.
test "PackedIntArray at end of available memory" {
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.linux, .macosx, .ios, .freebsd, .netbsd, .windows => {},
else => return,
}
@@ -612,7 +612,7 @@ test "PackedIntArray at end of available memory" {
}
test "PackedIntSlice at end of available memory" {
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.linux, .macosx, .ios, .freebsd, .netbsd, .windows => {},
else => return,
}
diff --git a/lib/std/process.zig b/lib/std/process.zig
index 0dab8bb64b..01b9947518 100644
--- a/lib/std/process.zig
+++ b/lib/std/process.zig
@@ -36,7 +36,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
var result = BufMap.init(allocator);
errdefer result.deinit();
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const ptr = os.windows.peb().ProcessParameters.Environment;
var i: usize = 0;
@@ -61,7 +61,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
try result.setMove(key, value);
}
return result;
- } else if (builtin.os == .wasi) {
+ } else if (builtin.os.tag == .wasi) {
var environ_count: usize = undefined;
var environ_buf_size: usize = undefined;
@@ -137,7 +137,7 @@ pub const GetEnvVarOwnedError = error{
/// Caller must free returned memory.
pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const result_w = blk: {
const key_w = try std.unicode.utf8ToUtf16LeWithNull(allocator, key);
defer allocator.free(key_w);
@@ -338,12 +338,12 @@ pub const ArgIteratorWindows = struct {
};
pub const ArgIterator = struct {
- const InnerType = if (builtin.os == .windows) ArgIteratorWindows else ArgIteratorPosix;
+ const InnerType = if (builtin.os.tag == .windows) ArgIteratorWindows else ArgIteratorPosix;
inner: InnerType,
pub fn init() ArgIterator {
- if (builtin.os == .wasi) {
+ if (builtin.os.tag == .wasi) {
// TODO: Figure out a compatible interface accomodating WASI
@compileError("ArgIterator is not yet supported in WASI. Use argsAlloc and argsFree instead.");
}
@@ -355,7 +355,7 @@ pub const ArgIterator = struct {
/// You must free the returned memory when done.
pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
return self.inner.next(allocator);
} else {
return mem.dupe(allocator, u8, self.inner.next() orelse return null);
@@ -380,7 +380,7 @@ pub fn args() ArgIterator {
/// Caller must call argsFree on result.
pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 {
- if (builtin.os == .wasi) {
+ if (builtin.os.tag == .wasi) {
var count: usize = undefined;
var buf_size: usize = undefined;
@@ -445,7 +445,7 @@ pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 {
}
pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void {
- if (builtin.os == .wasi) {
+ if (builtin.os.tag == .wasi) {
const last_item = args_alloc[args_alloc.len - 1];
const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated
const first_item_ptr = args_alloc[0].ptr;
@@ -498,7 +498,7 @@ pub const UserInfo = struct {
/// POSIX function which gets a uid from username.
pub fn getUserInfo(name: []const u8) !UserInfo {
- return switch (builtin.os) {
+ return switch (builtin.os.tag) {
.linux, .macosx, .watchos, .tvos, .ios, .freebsd, .netbsd => posixGetUserInfo(name),
else => @compileError("Unsupported OS"),
};
@@ -591,7 +591,7 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
}
pub fn getBaseAddress() usize {
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.linux => {
const base = os.system.getauxval(std.elf.AT_BASE);
if (base != 0) {
@@ -609,13 +609,17 @@ pub fn getBaseAddress() usize {
}
/// Caller owns the result value and each inner slice.
+/// TODO Remove the `Allocator` requirement from this API, which will remove the `Allocator`
+/// requirement from `std.zig.system.NativeTargetInfo.detect`. Most likely this will require
+/// introducing a new, lower-level function which takes a callback function, and then this
+/// function which takes an allocator can exist on top of it.
pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]u8 {
switch (builtin.link_mode) {
.Static => return &[_][:0]u8{},
.Dynamic => {},
}
const List = std.ArrayList([:0]u8);
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.linux,
.freebsd,
.netbsd,
diff --git a/lib/std/reset_event.zig b/lib/std/reset_event.zig
index b31906c5f8..c28db809ca 100644
--- a/lib/std/reset_event.zig
+++ b/lib/std/reset_event.zig
@@ -16,7 +16,7 @@ pub const ResetEvent = struct {
pub const OsEvent = if (builtin.single_threaded)
DebugEvent
- else if (builtin.link_libc and builtin.os != .windows and builtin.os != .linux)
+ else if (builtin.link_libc and builtin.os.tag != .windows and builtin.os.tag != .linux)
PosixEvent
else
AtomicEvent;
@@ -106,7 +106,7 @@ const PosixEvent = struct {
fn deinit(self: *PosixEvent) void {
// on dragonfly, *destroy() functions can return EINVAL
// for statically initialized pthread structures
- const err = if (builtin.os == .dragonfly) os.EINVAL else 0;
+ const err = if (builtin.os.tag == .dragonfly) os.EINVAL else 0;
const retm = c.pthread_mutex_destroy(&self.mutex);
assert(retm == 0 or retm == err);
@@ -215,7 +215,7 @@ const AtomicEvent = struct {
}
}
- pub const Futex = switch (builtin.os) {
+ pub const Futex = switch (builtin.os.tag) {
.windows => WindowsFutex,
.linux => LinuxFutex,
else => SpinFutex,
diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig
index 56ae3d0d8f..e0c2636df1 100644
--- a/lib/std/special/c.zig
+++ b/lib/std/special/c.zig
@@ -17,7 +17,7 @@ const is_msvc = switch (builtin.abi) {
.msvc => true,
else => false,
};
-const is_freestanding = switch (builtin.os) {
+const is_freestanding = switch (builtin.os.tag) {
.freestanding => true,
else => false,
};
@@ -47,7 +47,7 @@ fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.C) c_int {
}
fn strlen(s: [*:0]const u8) callconv(.C) usize {
- return std.mem.len(u8, s);
+ return std.mem.len(s);
}
fn strncmp(_l: [*:0]const u8, _r: [*:0]const u8, _n: usize) callconv(.C) c_int {
@@ -81,7 +81,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn
@setCold(true);
std.debug.panic("{}", .{msg});
}
- if (builtin.os != .freestanding and builtin.os != .other) {
+ if (builtin.os.tag != .freestanding and builtin.os.tag != .other) {
std.os.abort();
}
while (true) {}
@@ -178,11 +178,11 @@ test "test_bcmp" {
comptime {
if (builtin.mode != builtin.Mode.ReleaseFast and
builtin.mode != builtin.Mode.ReleaseSmall and
- builtin.os != builtin.Os.windows)
+ builtin.os.tag != .windows)
{
@export(__stack_chk_fail, .{ .name = "__stack_chk_fail" });
}
- if (builtin.os == builtin.Os.linux) {
+ if (builtin.os.tag == .linux) {
@export(clone, .{ .name = "clone" });
}
}
diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig
index 8d49fdbd2a..9ed866f62d 100644
--- a/lib/std/special/compiler_rt.zig
+++ b/lib/std/special/compiler_rt.zig
@@ -1,11 +1,9 @@
-const builtin = @import("builtin");
+const std = @import("std");
+const builtin = std.builtin;
const is_test = builtin.is_test;
-const is_gnu = switch (builtin.abi) {
- .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true,
- else => false,
-};
-const is_mingw = builtin.os == .windows and is_gnu;
+const is_gnu = std.Target.current.abi.isGnu();
+const is_mingw = builtin.os.tag == .windows and is_gnu;
comptime {
const linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak;
@@ -180,7 +178,7 @@ comptime {
@export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage });
@export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr8", .linkage = linkage });
- if (builtin.os == .linux) {
+ if (builtin.os.tag == .linux) {
@export(@import("compiler_rt/arm.zig").__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage });
}
@@ -250,7 +248,7 @@ comptime {
@export(@import("compiler_rt/aullrem.zig")._aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage });
}
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
// Default stack-probe functions emitted by LLVM
if (is_mingw) {
@export(@import("compiler_rt/stack_probe.zig")._chkstk, .{ .name = "_alloca", .linkage = strong_linkage });
@@ -288,7 +286,7 @@ comptime {
else => {},
}
} else {
- if (builtin.glibc_version != null) {
+ if (std.Target.current.isGnuLibC() and builtin.link_libc) {
@export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = linkage });
}
@export(@import("compiler_rt/divti3.zig").__divti3, .{ .name = "__divti3", .linkage = linkage });
@@ -307,7 +305,7 @@ comptime {
pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
@setCold(true);
if (is_test) {
- @import("std").debug.panic("{}", .{msg});
+ std.debug.panic("{}", .{msg});
} else {
unreachable;
}
diff --git a/lib/std/special/compiler_rt/ashlti3.zig b/lib/std/special/compiler_rt/ashlti3.zig
index 211515f9dd..e3bc60bbb9 100644
--- a/lib/std/special/compiler_rt/ashlti3.zig
+++ b/lib/std/special/compiler_rt/ashlti3.zig
@@ -24,7 +24,7 @@ const twords = extern union {
all: i128,
s: S,
- const S = if (builtin.endian == builtin.Endian.Little)
+ const S = if (builtin.endian == .Little)
struct {
low: u64,
high: u64,
diff --git a/lib/std/special/compiler_rt/ashrti3.zig b/lib/std/special/compiler_rt/ashrti3.zig
index 1bcd40d2e4..1cbe24fdeb 100644
--- a/lib/std/special/compiler_rt/ashrti3.zig
+++ b/lib/std/special/compiler_rt/ashrti3.zig
@@ -25,7 +25,7 @@ const twords = extern union {
all: i128,
s: S,
- const S = if (builtin.endian == builtin.Endian.Little)
+ const S = if (builtin.endian == .Little)
struct {
low: i64,
high: i64,
diff --git a/lib/std/special/compiler_rt/extendXfYf2_test.zig b/lib/std/special/compiler_rt/extendXfYf2_test.zig
index aa2faae901..e2664f6bae 100644
--- a/lib/std/special/compiler_rt/extendXfYf2_test.zig
+++ b/lib/std/special/compiler_rt/extendXfYf2_test.zig
@@ -90,7 +90,7 @@ test "extendhfsf2" {
test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN
// On x86 the NaN becomes quiet because the return is pushed on the x87
// stack due to ABI requirements
- if (builtin.arch != .i386 and builtin.os == .windows)
+ if (builtin.arch != .i386 and builtin.os.tag == .windows)
test__extendhfsf2(0x7c01, 0x7f802000); // sNaN
test__extendhfsf2(0, 0); // 0
diff --git a/lib/std/special/compiler_rt/lshrti3.zig b/lib/std/special/compiler_rt/lshrti3.zig
index 043be5e2a2..e1c2bb5bd3 100644
--- a/lib/std/special/compiler_rt/lshrti3.zig
+++ b/lib/std/special/compiler_rt/lshrti3.zig
@@ -24,7 +24,7 @@ const twords = extern union {
all: i128,
s: S,
- const S = if (builtin.endian == builtin.Endian.Little)
+ const S = if (builtin.endian == .Little)
struct {
low: u64,
high: u64,
diff --git a/lib/std/special/compiler_rt/multi3.zig b/lib/std/special/compiler_rt/multi3.zig
index f46bd55421..eba58c45fc 100644
--- a/lib/std/special/compiler_rt/multi3.zig
+++ b/lib/std/special/compiler_rt/multi3.zig
@@ -45,7 +45,7 @@ const twords = extern union {
all: i128,
s: S,
- const S = if (builtin.endian == builtin.Endian.Little)
+ const S = if (builtin.endian == .Little)
struct {
low: u64,
high: u64,
diff --git a/lib/std/special/compiler_rt/truncXfYf2.zig b/lib/std/special/compiler_rt/truncXfYf2.zig
index 7c83c66ec0..cba5b85264 100644
--- a/lib/std/special/compiler_rt/truncXfYf2.zig
+++ b/lib/std/special/compiler_rt/truncXfYf2.zig
@@ -1,23 +1,23 @@
const std = @import("std");
pub fn __truncsfhf2(a: f32) callconv(.C) u16 {
- return @bitCast(u16, truncXfYf2(f16, f32, a));
+ return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f32, a }));
}
pub fn __truncdfhf2(a: f64) callconv(.C) u16 {
- return @bitCast(u16, truncXfYf2(f16, f64, a));
+ return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f64, a }));
}
pub fn __trunctfsf2(a: f128) callconv(.C) f32 {
- return truncXfYf2(f32, f128, a);
+ return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f128, a });
}
pub fn __trunctfdf2(a: f128) callconv(.C) f64 {
- return truncXfYf2(f64, f128, a);
+ return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f64, f128, a });
}
pub fn __truncdfsf2(a: f64) callconv(.C) f32 {
- return truncXfYf2(f32, f64, a);
+ return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f64, a });
}
pub fn __aeabi_d2f(a: f64) callconv(.AAPCS) f32 {
@@ -35,7 +35,7 @@ pub fn __aeabi_f2h(a: f32) callconv(.AAPCS) u16 {
return @call(.{ .modifier = .always_inline }, __truncsfhf2, .{a});
}
-inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
+fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
const src_rep_t = std.meta.IntType(false, @typeInfo(src_t).Float.bits);
const dst_rep_t = std.meta.IntType(false, @typeInfo(dst_t).Float.bits);
const srcSigBits = std.math.floatMantissaBits(src_t);
diff --git a/lib/std/special/compiler_rt/udivmod.zig b/lib/std/special/compiler_rt/udivmod.zig
index 1cf2589b16..9f0fb7d50f 100644
--- a/lib/std/special/compiler_rt/udivmod.zig
+++ b/lib/std/special/compiler_rt/udivmod.zig
@@ -2,8 +2,8 @@ const builtin = @import("builtin");
const is_test = builtin.is_test;
const low = switch (builtin.endian) {
- builtin.Endian.Big => 1,
- builtin.Endian.Little => 0,
+ .Big => 1,
+ .Little => 0,
};
const high = 1 - low;
diff --git a/lib/std/special/init-exe/build.zig b/lib/std/special/init-exe/build.zig
index 0b7410f2ad..fd71588c5f 100644
--- a/lib/std/special/init-exe/build.zig
+++ b/lib/std/special/init-exe/build.zig
@@ -1,8 +1,18 @@
const Builder = @import("std").build.Builder;
pub fn build(b: *Builder) void {
+ // Standard target options allows the person running `zig build` to choose
+ // what target to build for. Here we do not override the defaults, which
+ // means any target is allowed, and the default is native. Other options
+ // for restricting supported target set are available.
+ const target = b.standardTargetOptions(.{});
+
+ // Standard release options allow the person running `zig build` to select
+ // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
+
const exe = b.addExecutable("$", "src/main.zig");
+ exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();
diff --git a/lib/std/special/init-exe/src/main.zig b/lib/std/special/init-exe/src/main.zig
index 5f35540dc0..c6a70af56d 100644
--- a/lib/std/special/init-exe/src/main.zig
+++ b/lib/std/special/init-exe/src/main.zig
@@ -1,5 +1,5 @@
const std = @import("std");
pub fn main() anyerror!void {
- std.debug.warn("All your base are belong to us.\n", .{});
+ std.debug.warn("All your codebase are belong to us.\n", .{});
}
diff --git a/lib/std/spinlock.zig b/lib/std/spinlock.zig
index 1a3239a95c..0af08e9a84 100644
--- a/lib/std/spinlock.zig
+++ b/lib/std/spinlock.zig
@@ -46,7 +46,7 @@ pub const SpinLock = struct {
// and yielding for 380-410 iterations was found to be
// a nice sweet spot. Posix systems on the other hand,
// especially linux, perform better by yielding the thread.
- switch (builtin.os) {
+ switch (builtin.os.tag) {
.windows => loopHint(400),
else => std.os.sched_yield() catch loopHint(1),
}
diff --git a/lib/std/start.zig b/lib/std/start.zig
index b58b6e8144..b8e3e97f94 100644
--- a/lib/std/start.zig
+++ b/lib/std/start.zig
@@ -12,7 +12,7 @@ const start_sym_name = if (builtin.arch.isMIPS()) "__start" else "_start";
comptime {
if (builtin.output_mode == .Lib and builtin.link_mode == .Dynamic) {
- if (builtin.os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) {
+ if (builtin.os.tag == .windows and !@hasDecl(root, "_DllMainCRTStartup")) {
@export(_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" });
}
} else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) {
@@ -20,17 +20,17 @@ comptime {
if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {
@export(main, .{ .name = "main", .linkage = .Weak });
}
- } else if (builtin.os == .windows) {
+ } else if (builtin.os.tag == .windows) {
if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and
!@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup"))
{
@export(WinMainCRTStartup, .{ .name = "WinMainCRTStartup" });
}
- } else if (builtin.os == .uefi) {
+ } else if (builtin.os.tag == .uefi) {
if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" });
- } else if (builtin.arch.isWasm() and builtin.os == .freestanding) {
+ } else if (builtin.arch.isWasm() and builtin.os.tag == .freestanding) {
if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name });
- } else if (builtin.os != .other and builtin.os != .freestanding) {
+ } else if (builtin.os.tag != .other and builtin.os.tag != .freestanding) {
if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name });
}
}
@@ -78,7 +78,7 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv
}
fn _start() callconv(.Naked) noreturn {
- if (builtin.os == builtin.Os.wasi) {
+ if (builtin.os.tag == .wasi) {
// This is marked inline because for some reason LLVM in release mode fails to inline it,
// and we want fewer call frames in stack traces.
std.os.wasi.proc_exit(@call(.{ .modifier = .always_inline }, callMain, .{}));
@@ -133,7 +133,7 @@ fn WinMainCRTStartup() callconv(.Stdcall) noreturn {
// TODO https://github.com/ziglang/zig/issues/265
fn posixCallMainAndExit() noreturn {
- if (builtin.os == builtin.Os.freebsd) {
+ if (builtin.os.tag == .freebsd) {
@setAlignStack(16);
}
const argc = starting_stack_ptr[0];
@@ -144,7 +144,7 @@ fn posixCallMainAndExit() noreturn {
while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
const envp = @ptrCast([*][*:0]u8, envp_optional)[0..envp_count];
- if (builtin.os == .linux) {
+ if (builtin.os.tag == .linux) {
// Find the beginning of the auxiliary vector
const auxv = @ptrCast([*]std.elf.Auxv, @alignCast(@alignOf(usize), envp.ptr + envp_count + 1));
std.os.linux.elf_aux_maybe = auxv;
diff --git a/lib/std/target.zig b/lib/std/target.zig
index ba61a2c5f0..778a93b90d 100644
--- a/lib/std/target.zig
+++ b/lib/std/target.zig
@@ -1,61 +1,291 @@
const std = @import("std.zig");
const mem = std.mem;
const builtin = std.builtin;
+const Version = std.builtin.Version;
/// TODO Nearly all the functions in this namespace would be
/// better off if https://github.com/ziglang/zig/issues/425
/// was solved.
-pub const Target = union(enum) {
- Native: void,
- Cross: Cross,
+pub const Target = struct {
+ cpu: Cpu,
+ os: Os,
+ abi: Abi,
- pub const Os = enum {
- freestanding,
- ananas,
- cloudabi,
- dragonfly,
- freebsd,
- fuchsia,
- ios,
- kfreebsd,
- linux,
- lv2,
- macosx,
- netbsd,
- openbsd,
- solaris,
- windows,
- haiku,
- minix,
- rtems,
- nacl,
- cnk,
- aix,
- cuda,
- nvcl,
- amdhsa,
- ps4,
- elfiamcu,
- tvos,
- watchos,
- mesa3d,
- contiki,
- amdpal,
- hermit,
- hurd,
- wasi,
- emscripten,
- uefi,
- other,
+ pub const Os = struct {
+ tag: Tag,
+ version_range: VersionRange,
- pub fn parse(text: []const u8) !Os {
- const info = @typeInfo(Os);
- inline for (info.Enum.fields) |field| {
- if (mem.eql(u8, text, field.name)) {
- return @field(Os, field.name);
+ pub const Tag = enum {
+ freestanding,
+ ananas,
+ cloudabi,
+ dragonfly,
+ freebsd,
+ fuchsia,
+ ios,
+ kfreebsd,
+ linux,
+ lv2,
+ macosx,
+ netbsd,
+ openbsd,
+ solaris,
+ windows,
+ haiku,
+ minix,
+ rtems,
+ nacl,
+ cnk,
+ aix,
+ cuda,
+ nvcl,
+ amdhsa,
+ ps4,
+ elfiamcu,
+ tvos,
+ watchos,
+ mesa3d,
+ contiki,
+ amdpal,
+ hermit,
+ hurd,
+ wasi,
+ emscripten,
+ uefi,
+ other,
+
+ pub fn isDarwin(tag: Tag) bool {
+ return switch (tag) {
+ .ios, .macosx, .watchos, .tvos => true,
+ else => false,
+ };
+ }
+
+ pub fn dynamicLibSuffix(tag: Tag) [:0]const u8 {
+ if (tag.isDarwin()) {
+ return ".dylib";
+ }
+ switch (tag) {
+ .windows => return ".dll",
+ else => return ".so",
}
}
- return error.UnknownOperatingSystem;
+ };
+
+ /// Based on NTDDI version constants from
+ /// https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt
+ pub const WindowsVersion = enum(u32) {
+ nt4 = 0x04000000,
+ win2k = 0x05000000,
+ xp = 0x05010000,
+ ws2003 = 0x05020000,
+ vista = 0x06000000,
+ win7 = 0x06010000,
+ win8 = 0x06020000,
+ win8_1 = 0x06030000,
+ win10 = 0x0A000000,
+ win10_th2 = 0x0A000001,
+ win10_rs1 = 0x0A000002,
+ win10_rs2 = 0x0A000003,
+ win10_rs3 = 0x0A000004,
+ win10_rs4 = 0x0A000005,
+ win10_rs5 = 0x0A000006,
+ win10_19h1 = 0x0A000007,
+ _,
+
+ pub const Range = struct {
+ min: WindowsVersion,
+ max: WindowsVersion,
+
+ pub fn includesVersion(self: Range, ver: WindowsVersion) bool {
+ return @enumToInt(ver) >= @enumToInt(self.min) and @enumToInt(ver) <= @enumToInt(self.max);
+ }
+ };
+ };
+
+ pub const LinuxVersionRange = struct {
+ range: Version.Range,
+ glibc: Version,
+
+ pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool {
+ return self.range.includesVersion(ver);
+ }
+ };
+
+ /// The version ranges here represent the minimum OS version to be supported
+ /// and the maximum OS version to be supported. The default values represent
+ /// the range that the Zig Standard Library bases its abstractions on.
+ ///
+ /// The minimum version of the range is the main setting to tweak for a target.
+ /// Usually, the maximum target OS version will remain the default, which is
+ /// the latest released version of the OS.
+ ///
+ /// To test at compile time if the target is guaranteed to support a given OS feature,
+ /// one should check that the minimum version of the range is greater than or equal to
+ /// the version the feature was introduced in.
+ ///
+ /// To test at compile time if the target certainly will not support a given OS feature,
+ /// one should check that the maximum version of the range is less than the version the
+ /// feature was introduced in.
+ ///
+ /// If neither of these cases apply, a runtime check should be used to determine if the
+ /// target supports a given OS feature.
+ ///
+ /// Binaries built with a given maximum version will continue to function on newer operating system
+ /// versions. However, such a binary may not take full advantage of the newer operating system APIs.
+ pub const VersionRange = union {
+ none: void,
+ semver: Version.Range,
+ linux: LinuxVersionRange,
+ windows: WindowsVersion.Range,
+
+ /// The default `VersionRange` represents the range that the Zig Standard Library
+ /// bases its abstractions on.
+ pub fn default(tag: Tag) VersionRange {
+ switch (tag) {
+ .freestanding,
+ .ananas,
+ .cloudabi,
+ .dragonfly,
+ .fuchsia,
+ .kfreebsd,
+ .lv2,
+ .solaris,
+ .haiku,
+ .minix,
+ .rtems,
+ .nacl,
+ .cnk,
+ .aix,
+ .cuda,
+ .nvcl,
+ .amdhsa,
+ .ps4,
+ .elfiamcu,
+ .mesa3d,
+ .contiki,
+ .amdpal,
+ .hermit,
+ .hurd,
+ .wasi,
+ .emscripten,
+ .uefi,
+ .other,
+ => return .{ .none = {} },
+
+ .freebsd => return .{
+ .semver = Version.Range{
+ .min = .{ .major = 12, .minor = 0 },
+ .max = .{ .major = 12, .minor = 1 },
+ },
+ },
+ .macosx => return .{
+ .semver = .{
+ .min = .{ .major = 10, .minor = 13 },
+ .max = .{ .major = 10, .minor = 15, .patch = 3 },
+ },
+ },
+ .ios => return .{
+ .semver = .{
+ .min = .{ .major = 12, .minor = 0 },
+ .max = .{ .major = 13, .minor = 4, .patch = 0 },
+ },
+ },
+ .watchos => return .{
+ .semver = .{
+ .min = .{ .major = 6, .minor = 0 },
+ .max = .{ .major = 6, .minor = 2, .patch = 0 },
+ },
+ },
+ .tvos => return .{
+ .semver = .{
+ .min = .{ .major = 13, .minor = 0 },
+ .max = .{ .major = 13, .minor = 4, .patch = 0 },
+ },
+ },
+ .netbsd => return .{
+ .semver = .{
+ .min = .{ .major = 8, .minor = 0 },
+ .max = .{ .major = 9, .minor = 0 },
+ },
+ },
+ .openbsd => return .{
+ .semver = .{
+ .min = .{ .major = 6, .minor = 6 },
+ .max = .{ .major = 6, .minor = 6 },
+ },
+ },
+
+ .linux => return .{
+ .linux = .{
+ .range = .{
+ .min = .{ .major = 3, .minor = 16 },
+ .max = .{ .major = 5, .minor = 5, .patch = 5 },
+ },
+ .glibc = .{ .major = 2, .minor = 17 },
+ },
+ },
+
+ .windows => return .{
+ .windows = .{
+ .min = .win8_1,
+ .max = .win10_19h1,
+ },
+ },
+ }
+ }
+ };
+
+ pub fn defaultVersionRange(tag: Tag) Os {
+ return .{
+ .tag = tag,
+ .version_range = VersionRange.default(tag),
+ };
+ }
+
+ pub fn requiresLibC(os: Os) bool {
+ return switch (os.tag) {
+ .freebsd,
+ .netbsd,
+ .macosx,
+ .ios,
+ .tvos,
+ .watchos,
+ .dragonfly,
+ .openbsd,
+ => true,
+
+ .linux,
+ .windows,
+ .freestanding,
+ .ananas,
+ .cloudabi,
+ .fuchsia,
+ .kfreebsd,
+ .lv2,
+ .solaris,
+ .haiku,
+ .minix,
+ .rtems,
+ .nacl,
+ .cnk,
+ .aix,
+ .cuda,
+ .nvcl,
+ .amdhsa,
+ .ps4,
+ .elfiamcu,
+ .mesa3d,
+ .contiki,
+ .amdpal,
+ .hermit,
+ .hurd,
+ .wasi,
+ .emscripten,
+ .uefi,
+ .other,
+ => false,
+ };
}
};
@@ -98,11 +328,10 @@ pub const Target = union(enum) {
macabi,
pub fn default(arch: Cpu.Arch, target_os: Os) Abi {
- switch (arch) {
- .wasm32, .wasm64 => return .musl,
- else => {},
+ if (arch.isWasm()) {
+ return .musl;
}
- switch (target_os) {
+ switch (target_os.tag) {
.freestanding,
.ananas,
.cloudabi,
@@ -147,14 +376,25 @@ pub const Target = union(enum) {
}
}
- pub fn parse(text: []const u8) !Abi {
- const info = @typeInfo(Abi);
- inline for (info.Enum.fields) |field| {
- if (mem.eql(u8, text, field.name)) {
- return @field(Abi, field.name);
- }
- }
- return error.UnknownApplicationBinaryInterface;
+ pub fn isGnu(abi: Abi) bool {
+ return switch (abi) {
+ .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true,
+ else => false,
+ };
+ }
+
+ pub fn isMusl(abi: Abi) bool {
+ return switch (abi) {
+ .musl, .musleabi, .musleabihf => true,
+ else => false,
+ };
+ }
+
+ pub fn oFileExt(abi: Abi) [:0]const u8 {
+ return switch (abi) {
+ .msvc => ".obj",
+ else => ".o",
+ };
}
};
@@ -177,12 +417,6 @@ pub const Target = union(enum) {
EfiRuntimeDriver,
};
- pub const Cross = struct {
- cpu: Cpu,
- os: Os,
- abi: Abi,
- };
-
pub const Cpu = struct {
/// Architecture
arch: Arch,
@@ -228,6 +462,12 @@ pub const Target = union(enum) {
return Set{ .ints = [1]usize{0} ** usize_count };
}
+ pub fn isEmpty(set: Set) bool {
+ return for (set.ints) |x| {
+ if (x != 0) break false;
+ } else true;
+ }
+
pub fn isEnabled(set: Set, arch_feature_index: Index) bool {
const usize_index = arch_feature_index / @bitSizeOf(usize);
const bit_index = @intCast(ShiftInt, arch_feature_index % @bitSizeOf(usize));
@@ -254,6 +494,15 @@ pub const Target = union(enum) {
set.ints[usize_index] &= ~(@as(usize, 1) << bit_index);
}
+ /// Removes the specified feature but not its dependents.
+ pub fn removeFeatureSet(set: *Set, other_set: Set) void {
+ // TODO should be able to use binary not on @Vector type.
+ // https://github.com/ziglang/zig/issues/903
+ for (set.ints) |*int, i| {
+ int.* &= ~other_set.ints[i];
+ }
+ }
+
pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void {
@setEvalBranchQuota(1000000);
@@ -392,7 +641,7 @@ pub const Target = union(enum) {
return cpu;
}
}
- return error.UnknownCpu;
+ return error.UnknownCpuModel;
}
pub fn toElfMachine(arch: Arch) std.elf.EM {
@@ -508,6 +757,67 @@ pub const Target = union(enum) {
};
}
+ pub fn ptrBitWidth(arch: Arch) u32 {
+ switch (arch) {
+ .avr,
+ .msp430,
+ => return 16,
+
+ .arc,
+ .arm,
+ .armeb,
+ .hexagon,
+ .le32,
+ .mips,
+ .mipsel,
+ .powerpc,
+ .r600,
+ .riscv32,
+ .sparc,
+ .sparcel,
+ .tce,
+ .tcele,
+ .thumb,
+ .thumbeb,
+ .i386,
+ .xcore,
+ .nvptx,
+ .amdil,
+ .hsail,
+ .spir,
+ .kalimba,
+ .shave,
+ .lanai,
+ .wasm32,
+ .renderscript32,
+ .aarch64_32,
+ => return 32,
+
+ .aarch64,
+ .aarch64_be,
+ .mips64,
+ .mips64el,
+ .powerpc64,
+ .powerpc64le,
+ .riscv64,
+ .x86_64,
+ .nvptx64,
+ .le64,
+ .amdil64,
+ .hsail64,
+ .spir64,
+ .wasm64,
+ .renderscript64,
+ .amdgcn,
+ .bpfel,
+ .bpfeb,
+ .sparcv9,
+ .s390x,
+ .ve,
+ => return 64,
+ }
+ }
+
/// Returns a name that matches the lib/std/target/* directory name.
pub fn genericName(arch: Arch) []const u8 {
return switch (arch) {
@@ -575,16 +885,6 @@ pub const Target = union(enum) {
else => &[0]*const Model{},
};
}
-
- pub fn parse(text: []const u8) !Arch {
- const info = @typeInfo(Arch);
- inline for (info.Enum.fields) |field| {
- if (mem.eql(u8, text, field.name)) {
- return @as(Arch, @field(Arch, field.name));
- }
- }
- return error.UnknownArchitecture;
- }
};
pub const Model = struct {
@@ -601,525 +901,172 @@ pub const Target = union(enum) {
.features = features,
};
}
+
+ pub fn baseline(arch: Arch) *const Model {
+ const S = struct {
+ const generic_model = Model{
+ .name = "generic",
+ .llvm_name = null,
+ .features = Cpu.Feature.Set.empty,
+ };
+ };
+ return switch (arch) {
+ .arm, .armeb, .thumb, .thumbeb => &arm.cpu.baseline,
+ .aarch64, .aarch64_be, .aarch64_32 => &aarch64.cpu.generic,
+ .avr => &avr.cpu.avr1,
+ .bpfel, .bpfeb => &bpf.cpu.generic,
+ .hexagon => &hexagon.cpu.generic,
+ .mips, .mipsel => &mips.cpu.mips32,
+ .mips64, .mips64el => &mips.cpu.mips64,
+ .msp430 => &msp430.cpu.generic,
+ .powerpc, .powerpc64, .powerpc64le => &powerpc.cpu.generic,
+ .amdgcn => &amdgpu.cpu.generic,
+ .riscv32 => &riscv.cpu.baseline_rv32,
+ .riscv64 => &riscv.cpu.baseline_rv64,
+ .sparc, .sparcv9, .sparcel => &sparc.cpu.generic,
+ .s390x => &systemz.cpu.generic,
+ .i386 => &x86.cpu.pentium4,
+ .x86_64 => &x86.cpu.x86_64,
+ .nvptx, .nvptx64 => &nvptx.cpu.sm_20,
+ .wasm32, .wasm64 => &wasm.cpu.generic,
+
+ else => &S.generic_model,
+ };
+ }
};
/// The "default" set of CPU features for cross-compiling. A conservative set
/// of features that is expected to be supported on most available hardware.
pub fn baseline(arch: Arch) Cpu {
- const S = struct {
- const generic_model = Model{
- .name = "generic",
- .llvm_name = null,
- .features = Cpu.Feature.Set.empty,
- };
- };
- const model = switch (arch) {
- .arm, .armeb, .thumb, .thumbeb => &arm.cpu.baseline,
- .aarch64, .aarch64_be, .aarch64_32 => &aarch64.cpu.generic,
- .avr => &avr.cpu.avr1,
- .bpfel, .bpfeb => &bpf.cpu.generic,
- .hexagon => &hexagon.cpu.generic,
- .mips, .mipsel => &mips.cpu.mips32,
- .mips64, .mips64el => &mips.cpu.mips64,
- .msp430 => &msp430.cpu.generic,
- .powerpc, .powerpc64, .powerpc64le => &powerpc.cpu.generic,
- .amdgcn => &amdgpu.cpu.generic,
- .riscv32 => &riscv.cpu.baseline_rv32,
- .riscv64 => &riscv.cpu.baseline_rv64,
- .sparc, .sparcv9, .sparcel => &sparc.cpu.generic,
- .s390x => &systemz.cpu.generic,
- .i386 => &x86.cpu.pentium4,
- .x86_64 => &x86.cpu.x86_64,
- .nvptx, .nvptx64 => &nvptx.cpu.sm_20,
- .wasm32, .wasm64 => &wasm.cpu.generic,
-
- else => &S.generic_model,
- };
- return model.toCpu(arch);
+ return Model.baseline(arch).toCpu(arch);
}
};
pub const current = Target{
- .Cross = Cross{
- .cpu = builtin.cpu,
- .os = builtin.os,
- .abi = builtin.abi,
- },
+ .cpu = builtin.cpu,
+ .os = builtin.os,
+ .abi = builtin.abi,
};
pub const stack_align = 16;
- pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 {
- return std.fmt.allocPrint(allocator, "{}-{}-{}", .{
- @tagName(self.getArch()),
- @tagName(self.getOs()),
- @tagName(self.getAbi()),
- });
+ pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![:0]u8 {
+ return std.zig.CrossTarget.fromTarget(self).zigTriple(allocator);
}
- /// Returned slice must be freed by the caller.
- pub fn vcpkgTriplet(allocator: *mem.Allocator, target: Target, linkage: std.build.VcpkgLinkage) ![]const u8 {
- const arch = switch (target.getArch()) {
- .i386 => "x86",
- .x86_64 => "x64",
+ pub fn linuxTripleSimple(allocator: *mem.Allocator, cpu_arch: Cpu.Arch, os_tag: Os.Tag, abi: Abi) ![:0]u8 {
+ return std.fmt.allocPrint0(allocator, "{}-{}-{}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) });
+ }
- .arm,
- .armeb,
- .thumb,
- .thumbeb,
- .aarch64_32,
- => "arm",
+ pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![:0]u8 {
+ return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi);
+ }
- .aarch64,
- .aarch64_be,
- => "arm64",
+ pub fn oFileExt(self: Target) [:0]const u8 {
+ return self.abi.oFileExt();
+ }
- else => return error.VcpkgNoSuchArchitecture,
- };
-
- const os = switch (target.getOs()) {
- .windows => "windows",
- .linux => "linux",
- .macosx => "macos",
- else => return error.VcpkgNoSuchOs,
- };
-
- if (linkage == .Static) {
- return try mem.join(allocator, "-", &[_][]const u8{ arch, os, "static" });
- } else {
- return try mem.join(allocator, "-", &[_][]const u8{ arch, os });
+ pub fn exeFileExtSimple(cpu_arch: Cpu.Arch, os_tag: Os.Tag) [:0]const u8 {
+ switch (os_tag) {
+ .windows => return ".exe",
+ .uefi => return ".efi",
+ else => if (cpu_arch.isWasm()) {
+ return ".wasm";
+ } else {
+ return "";
+ },
}
}
- pub fn allocDescription(self: Target, allocator: *mem.Allocator) ![]u8 {
- // TODO is there anything else worthy of the description that is not
- // already captured in the triple?
- return self.zigTriple(allocator);
+ pub fn exeFileExt(self: Target) [:0]const u8 {
+ return exeFileExtSimple(self.cpu.arch, self.os.tag);
}
- pub fn zigTripleNoSubArch(self: Target, allocator: *mem.Allocator) ![]u8 {
- return std.fmt.allocPrint(allocator, "{}-{}-{}", .{
- @tagName(self.getArch()),
- @tagName(self.getOs()),
- @tagName(self.getAbi()),
- });
- }
-
- pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 {
- return std.fmt.allocPrint(allocator, "{}-{}-{}", .{
- @tagName(self.getArch()),
- @tagName(self.getOs()),
- @tagName(self.getAbi()),
- });
- }
-
- pub const ParseOptions = struct {
- /// This is sometimes called a "triple". It looks roughly like this:
- /// riscv64-linux-gnu
- /// The fields are, respectively:
- /// * CPU Architecture
- /// * Operating System
- /// * C ABI (optional)
- arch_os_abi: []const u8,
-
- /// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e"
- /// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features
- /// to remove from the set.
- cpu_features: []const u8 = "baseline",
-
- /// If this is provided, the function will populate some information about parsing failures,
- /// so that user-friendly error messages can be delivered.
- diagnostics: ?*Diagnostics = null,
-
- pub const Diagnostics = struct {
- /// If the architecture was determined, this will be populated.
- arch: ?Cpu.Arch = null,
-
- /// If the OS was determined, this will be populated.
- os: ?Os = null,
-
- /// If the ABI was determined, this will be populated.
- abi: ?Abi = null,
-
- /// If the CPU name was determined, this will be populated.
- cpu_name: ?[]const u8 = null,
-
- /// If error.UnknownCpuFeature is returned, this will be populated.
- unknown_feature_name: ?[]const u8 = null,
- };
- };
-
- pub fn parse(args: ParseOptions) !Target {
- var dummy_diags: ParseOptions.Diagnostics = undefined;
- var diags = args.diagnostics orelse &dummy_diags;
-
- var it = mem.separate(args.arch_os_abi, "-");
- const arch_name = it.next() orelse return error.MissingArchitecture;
- const arch = try Cpu.Arch.parse(arch_name);
- diags.arch = arch;
-
- const os_name = it.next() orelse return error.MissingOperatingSystem;
- const os = try Os.parse(os_name);
- diags.os = os;
-
- const abi_name = it.next();
- const abi = if (abi_name) |n| try Abi.parse(n) else Abi.default(arch, os);
- diags.abi = abi;
-
- if (it.next() != null) return error.UnexpectedExtraField;
-
- const all_features = arch.allFeaturesList();
- var index: usize = 0;
- while (index < args.cpu_features.len and
- args.cpu_features[index] != '+' and
- args.cpu_features[index] != '-')
- {
- index += 1;
- }
- const cpu_name = args.cpu_features[0..index];
- diags.cpu_name = cpu_name;
-
- const cpu: Cpu = if (mem.eql(u8, cpu_name, "baseline")) Cpu.baseline(arch) else blk: {
- const cpu_model = try arch.parseCpuModel(cpu_name);
-
- var set = cpu_model.features;
- while (index < args.cpu_features.len) {
- const op = args.cpu_features[index];
- index += 1;
- const start = index;
- while (index < args.cpu_features.len and
- args.cpu_features[index] != '+' and
- args.cpu_features[index] != '-')
- {
- index += 1;
- }
- const feature_name = args.cpu_features[start..index];
- for (all_features) |feature, feat_index_usize| {
- const feat_index = @intCast(Cpu.Feature.Set.Index, feat_index_usize);
- if (mem.eql(u8, feature_name, feature.name)) {
- switch (op) {
- '+' => set.addFeature(feat_index),
- '-' => set.removeFeature(feat_index),
- else => unreachable,
- }
- break;
- }
- } else {
- diags.unknown_feature_name = feature_name;
- return error.UnknownCpuFeature;
- }
- }
- set.populateDependencies(all_features);
- break :blk .{
- .arch = arch,
- .model = cpu_model,
- .features = set,
- };
- };
- var cross = Cross{
- .cpu = cpu,
- .os = os,
- .abi = abi,
- };
- return Target{ .Cross = cross };
- }
-
- pub fn oFileExt(self: Target) []const u8 {
- return switch (self.getAbi()) {
- .msvc => ".obj",
- else => ".o",
- };
- }
-
- pub fn exeFileExt(self: Target) []const u8 {
- if (self.isWindows()) {
- return ".exe";
- } else if (self.isUefi()) {
- return ".efi";
- } else if (self.isWasm()) {
- return ".wasm";
- } else {
- return "";
- }
- }
-
- pub fn staticLibSuffix(self: Target) []const u8 {
- if (self.isWasm()) {
+ pub fn staticLibSuffix_cpu_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) [:0]const u8 {
+ if (cpu_arch.isWasm()) {
return ".wasm";
}
- switch (self.getAbi()) {
+ switch (abi) {
.msvc => return ".lib",
else => return ".a",
}
}
- pub fn dynamicLibSuffix(self: Target) []const u8 {
- if (self.isDarwin()) {
- return ".dylib";
- }
- switch (self.getOs()) {
- .windows => return ".dll",
- else => return ".so",
- }
+ pub fn staticLibSuffix(self: Target) [:0]const u8 {
+ return staticLibSuffix_cpu_arch_abi(self.cpu.arch, self.abi);
}
- pub fn libPrefix(self: Target) []const u8 {
- if (self.isWasm()) {
+ pub fn dynamicLibSuffix(self: Target) [:0]const u8 {
+ return self.os.tag.dynamicLibSuffix();
+ }
+
+ pub fn libPrefix_cpu_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) [:0]const u8 {
+ if (cpu_arch.isWasm()) {
return "";
}
- switch (self.getAbi()) {
+ switch (abi) {
.msvc => return "",
else => return "lib",
}
}
- pub fn getOs(self: Target) Os {
- return switch (self) {
- .Native => builtin.os,
- .Cross => |t| t.os,
- };
+ pub fn libPrefix(self: Target) [:0]const u8 {
+ return libPrefix_cpu_arch_abi(self.cpu.arch, self.abi);
}
- pub fn getCpu(self: Target) Cpu {
- return switch (self) {
- .Native => builtin.cpu,
- .Cross => |cross| cross.cpu,
- };
- }
-
- pub fn getArch(self: Target) Cpu.Arch {
- return self.getCpu().arch;
- }
-
- pub fn getAbi(self: Target) Abi {
- switch (self) {
- .Native => return builtin.abi,
- .Cross => |t| return t.abi,
+ pub fn getObjectFormatSimple(os_tag: Os.Tag, cpu_arch: Cpu.Arch) ObjectFormat {
+ if (os_tag == .windows or os_tag == .uefi) {
+ return .coff;
+ } else if (os_tag.isDarwin()) {
+ return .macho;
}
+ if (cpu_arch.isWasm()) {
+ return .wasm;
+ }
+ return .elf;
}
pub fn getObjectFormat(self: Target) ObjectFormat {
- switch (self) {
- .Native => return @import("builtin").object_format,
- .Cross => blk: {
- if (self.isWindows() or self.isUefi()) {
- return .coff;
- } else if (self.isDarwin()) {
- return .macho;
- }
- if (self.isWasm()) {
- return .wasm;
- }
- return .elf;
- },
- }
+ return getObjectFormatSimple(self.os.tag, self.cpu.arch);
}
pub fn isMinGW(self: Target) bool {
- return self.isWindows() and self.isGnu();
+ return self.os.tag == .windows and self.isGnu();
}
pub fn isGnu(self: Target) bool {
- return switch (self.getAbi()) {
- .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true,
- else => false,
- };
+ return self.abi.isGnu();
}
pub fn isMusl(self: Target) bool {
- return switch (self.getAbi()) {
- .musl, .musleabi, .musleabihf => true,
- else => false,
- };
- }
-
- pub fn isDarwin(self: Target) bool {
- return switch (self.getOs()) {
- .ios, .macosx, .watchos, .tvos => true,
- else => false,
- };
- }
-
- pub fn isWindows(self: Target) bool {
- return switch (self.getOs()) {
- .windows => true,
- else => false,
- };
- }
-
- pub fn isLinux(self: Target) bool {
- return switch (self.getOs()) {
- .linux => true,
- else => false,
- };
+ return self.abi.isMusl();
}
pub fn isAndroid(self: Target) bool {
- return switch (self.getAbi()) {
+ return switch (self.abi) {
.android => true,
else => false,
};
}
- pub fn isDragonFlyBSD(self: Target) bool {
- return switch (self.getOs()) {
- .dragonfly => true,
- else => false,
- };
- }
-
- pub fn isUefi(self: Target) bool {
- return switch (self.getOs()) {
- .uefi => true,
- else => false,
- };
- }
-
pub fn isWasm(self: Target) bool {
- return switch (self.getArch()) {
- .wasm32, .wasm64 => true,
- else => false,
- };
+ return self.cpu.arch.isWasm();
}
- pub fn isFreeBSD(self: Target) bool {
- return switch (self.getOs()) {
- .freebsd => true,
- else => false,
- };
+ pub fn isDarwin(self: Target) bool {
+ return self.os.tag.isDarwin();
}
- pub fn isNetBSD(self: Target) bool {
- return switch (self.getOs()) {
- .netbsd => true,
- else => false,
- };
+ pub fn isGnuLibC_os_tag_abi(os_tag: Os.Tag, abi: Abi) bool {
+ return os_tag == .linux and abi.isGnu();
}
- pub fn wantSharedLibSymLinks(self: Target) bool {
- return !self.isWindows();
- }
-
- pub fn osRequiresLibC(self: Target) bool {
- return self.isDarwin() or self.isFreeBSD() or self.isNetBSD();
- }
-
- pub fn getArchPtrBitWidth(self: Target) u32 {
- switch (self.getArch()) {
- .avr,
- .msp430,
- => return 16,
-
- .arc,
- .arm,
- .armeb,
- .hexagon,
- .le32,
- .mips,
- .mipsel,
- .powerpc,
- .r600,
- .riscv32,
- .sparc,
- .sparcel,
- .tce,
- .tcele,
- .thumb,
- .thumbeb,
- .i386,
- .xcore,
- .nvptx,
- .amdil,
- .hsail,
- .spir,
- .kalimba,
- .shave,
- .lanai,
- .wasm32,
- .renderscript32,
- .aarch64_32,
- => return 32,
-
- .aarch64,
- .aarch64_be,
- .mips64,
- .mips64el,
- .powerpc64,
- .powerpc64le,
- .riscv64,
- .x86_64,
- .nvptx64,
- .le64,
- .amdil64,
- .hsail64,
- .spir64,
- .wasm64,
- .renderscript64,
- .amdgcn,
- .bpfel,
- .bpfeb,
- .sparcv9,
- .s390x,
- .ve,
- => return 64,
- }
+ pub fn isGnuLibC(self: Target) bool {
+ return isGnuLibC_os_tag_abi(self.os.tag, self.abi);
}
pub fn supportsNewStackCall(self: Target) bool {
- return !self.isWasm();
- }
-
- pub const Executor = union(enum) {
- native,
- qemu: []const u8,
- wine: []const u8,
- wasmtime: []const u8,
- unavailable,
- };
-
- pub fn getExternalExecutor(self: Target) Executor {
- if (@as(@TagType(Target), self) == .Native) return .native;
-
- // If the target OS matches the host OS, we can use QEMU to emulate a foreign architecture.
- if (self.getOs() == builtin.os) {
- return switch (self.getArch()) {
- .aarch64 => Executor{ .qemu = "qemu-aarch64" },
- .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" },
- .arm => Executor{ .qemu = "qemu-arm" },
- .armeb => Executor{ .qemu = "qemu-armeb" },
- .i386 => Executor{ .qemu = "qemu-i386" },
- .mips => Executor{ .qemu = "qemu-mips" },
- .mipsel => Executor{ .qemu = "qemu-mipsel" },
- .mips64 => Executor{ .qemu = "qemu-mips64" },
- .mips64el => Executor{ .qemu = "qemu-mips64el" },
- .powerpc => Executor{ .qemu = "qemu-ppc" },
- .powerpc64 => Executor{ .qemu = "qemu-ppc64" },
- .powerpc64le => Executor{ .qemu = "qemu-ppc64le" },
- .riscv32 => Executor{ .qemu = "qemu-riscv32" },
- .riscv64 => Executor{ .qemu = "qemu-riscv64" },
- .s390x => Executor{ .qemu = "qemu-s390x" },
- .sparc => Executor{ .qemu = "qemu-sparc" },
- .x86_64 => Executor{ .qemu = "qemu-x86_64" },
- else => return .unavailable,
- };
- }
-
- if (self.isWindows()) {
- switch (self.getArchPtrBitWidth()) {
- 32 => return Executor{ .wine = "wine" },
- 64 => return Executor{ .wine = "wine64" },
- else => return .unavailable,
- }
- }
-
- if (self.getOs() == .wasi) {
- switch (self.getArchPtrBitWidth()) {
- 32 => return Executor{ .wasmtime = "wasmtime" },
- else => return .unavailable,
- }
- }
-
- return .unavailable;
+ return !self.cpu.arch.isWasm();
}
pub const FloatAbi = enum {
@@ -1129,7 +1076,7 @@ pub const Target = union(enum) {
};
pub fn getFloatAbi(self: Target) FloatAbi {
- return switch (self.getAbi()) {
+ return switch (self.abi) {
.gnueabihf,
.eabihf,
.musleabihf,
@@ -1139,13 +1086,10 @@ pub const Target = union(enum) {
}
pub fn hasDynamicLinker(self: Target) bool {
- switch (self.getArch()) {
- .wasm32,
- .wasm64,
- => return false,
- else => {},
+ if (self.cpu.arch.isWasm()) {
+ return false;
}
- switch (self.getOs()) {
+ switch (self.os.tag) {
.freestanding,
.ios,
.tvos,
@@ -1160,65 +1104,93 @@ pub const Target = union(enum) {
}
}
- /// Caller owns returned memory.
- pub fn getStandardDynamicLinkerPath(
- self: Target,
- allocator: *mem.Allocator,
- ) error{
- OutOfMemory,
- UnknownDynamicLinkerPath,
- TargetHasNoDynamicLinker,
- }![:0]u8 {
- const a = allocator;
+ pub const DynamicLinker = struct {
+ /// Contains the memory used to store the dynamic linker path. This field should
+ /// not be used directly. See `get` and `set`. This field exists so that this API requires no allocator.
+ buffer: [255]u8 = undefined,
+
+ /// Used to construct the dynamic linker path. This field should not be used
+ /// directly. See `get` and `set`.
+ max_byte: ?u8 = null,
+
+ /// Asserts that the length is less than or equal to 255 bytes.
+ pub fn init(dl_or_null: ?[]const u8) DynamicLinker {
+ var result: DynamicLinker = undefined;
+ result.set(dl_or_null);
+ return result;
+ }
+
+ /// The returned memory has the same lifetime as the `DynamicLinker`.
+ pub fn get(self: *const DynamicLinker) ?[]const u8 {
+ const m: usize = self.max_byte orelse return null;
+ return self.buffer[0 .. m + 1];
+ }
+
+ /// Asserts that the length is less than or equal to 255 bytes.
+ pub fn set(self: *DynamicLinker, dl_or_null: ?[]const u8) void {
+ if (dl_or_null) |dl| {
+ mem.copy(u8, &self.buffer, dl);
+ self.max_byte = @intCast(u8, dl.len - 1);
+ } else {
+ self.max_byte = null;
+ }
+ }
+ };
+
+ pub fn standardDynamicLinkerPath(self: Target) DynamicLinker {
+ var result: DynamicLinker = .{};
+ const S = struct {
+ fn print(r: *DynamicLinker, comptime fmt: []const u8, args: var) DynamicLinker {
+ r.max_byte = @intCast(u8, (std.fmt.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1);
+ return r.*;
+ }
+ fn copy(r: *DynamicLinker, s: []const u8) DynamicLinker {
+ mem.copy(u8, &r.buffer, s);
+ r.max_byte = @intCast(u8, s.len - 1);
+ return r.*;
+ }
+ };
+ const print = S.print;
+ const copy = S.copy;
+
if (self.isAndroid()) {
- return mem.dupeZ(a, u8, if (self.getArchPtrBitWidth() == 64)
- "/system/bin/linker64"
- else
- "/system/bin/linker");
+ const suffix = if (self.cpu.arch.ptrBitWidth() == 64) "64" else "";
+ return print(&result, "/system/bin/linker{}", .{suffix});
}
if (self.isMusl()) {
- var result = try std.Buffer.init(allocator, "/lib/ld-musl-");
- defer result.deinit();
-
- var is_arm = false;
- switch (self.getArch()) {
- .arm, .thumb => {
- try result.append("arm");
- is_arm = true;
- },
- .armeb, .thumbeb => {
- try result.append("armeb");
- is_arm = true;
- },
- else => |arch| try result.append(@tagName(arch)),
- }
- if (is_arm and self.getFloatAbi() == .hard) {
- try result.append("hf");
- }
- try result.append(".so.1");
- return result.toOwnedSlice();
+ const is_arm = switch (self.cpu.arch) {
+ .arm, .armeb, .thumb, .thumbeb => true,
+ else => false,
+ };
+ const arch_part = switch (self.cpu.arch) {
+ .arm, .thumb => "arm",
+ .armeb, .thumbeb => "armeb",
+ else => |arch| @tagName(arch),
+ };
+ const arch_suffix = if (is_arm and self.getFloatAbi() == .hard) "hf" else "";
+ return print(&result, "/lib/ld-musl-{}{}.so.1", .{ arch_part, arch_suffix });
}
- switch (self.getOs()) {
- .freebsd => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.1"),
- .netbsd => return mem.dupeZ(a, u8, "/libexec/ld.elf_so"),
- .dragonfly => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.2"),
- .linux => switch (self.getArch()) {
+ switch (self.os.tag) {
+ .freebsd => return copy(&result, "/libexec/ld-elf.so.1"),
+ .netbsd => return copy(&result, "/libexec/ld.elf_so"),
+ .dragonfly => return copy(&result, "/libexec/ld-elf.so.2"),
+ .linux => switch (self.cpu.arch) {
.i386,
.sparc,
.sparcel,
- => return mem.dupeZ(a, u8, "/lib/ld-linux.so.2"),
+ => return copy(&result, "/lib/ld-linux.so.2"),
- .aarch64 => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64.so.1"),
- .aarch64_be => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64_be.so.1"),
- .aarch64_32 => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64_32.so.1"),
+ .aarch64 => return copy(&result, "/lib/ld-linux-aarch64.so.1"),
+ .aarch64_be => return copy(&result, "/lib/ld-linux-aarch64_be.so.1"),
+ .aarch64_32 => return copy(&result, "/lib/ld-linux-aarch64_32.so.1"),
.arm,
.armeb,
.thumb,
.thumbeb,
- => return mem.dupeZ(a, u8, switch (self.getFloatAbi()) {
+ => return copy(&result, switch (self.getFloatAbi()) {
.hard => "/lib/ld-linux-armhf.so.3",
else => "/lib/ld-linux.so.3",
}),
@@ -1227,28 +1199,43 @@ pub const Target = union(enum) {
.mipsel,
.mips64,
.mips64el,
- => return error.UnknownDynamicLinkerPath,
+ => {
+ const lib_suffix = switch (self.abi) {
+ .gnuabin32, .gnux32 => "32",
+ .gnuabi64 => "64",
+ else => "",
+ };
+ const is_nan_2008 = mips.featureSetHas(self.cpu.features, .nan2008);
+ const loader = if (is_nan_2008) "ld-linux-mipsn8.so.1" else "ld.so.1";
+ return print(&result, "/lib{}/{}", .{ lib_suffix, loader });
+ },
- .powerpc => return mem.dupeZ(a, u8, "/lib/ld.so.1"),
- .powerpc64, .powerpc64le => return mem.dupeZ(a, u8, "/lib64/ld64.so.2"),
- .s390x => return mem.dupeZ(a, u8, "/lib64/ld64.so.1"),
- .sparcv9 => return mem.dupeZ(a, u8, "/lib64/ld-linux.so.2"),
- .x86_64 => return mem.dupeZ(a, u8, switch (self.getAbi()) {
+ .powerpc => return copy(&result, "/lib/ld.so.1"),
+ .powerpc64, .powerpc64le => return copy(&result, "/lib64/ld64.so.2"),
+ .s390x => return copy(&result, "/lib64/ld64.so.1"),
+ .sparcv9 => return copy(&result, "/lib64/ld-linux.so.2"),
+ .x86_64 => return copy(&result, switch (self.abi) {
.gnux32 => "/libx32/ld-linux-x32.so.2",
else => "/lib64/ld-linux-x86-64.so.2",
}),
- .riscv32 => return mem.dupeZ(a, u8, "/lib/ld-linux-riscv32-ilp32.so.1"),
- .riscv64 => return mem.dupeZ(a, u8, "/lib/ld-linux-riscv64-lp64.so.1"),
+ .riscv32 => return copy(&result, "/lib/ld-linux-riscv32-ilp32.so.1"),
+ .riscv64 => return copy(&result, "/lib/ld-linux-riscv64-lp64.so.1"),
+ // Architectures in this list have been verified as not having a standard
+ // dynamic linker path.
.wasm32,
.wasm64,
- => return error.TargetHasNoDynamicLinker,
-
- .arc,
- .avr,
.bpfel,
.bpfeb,
+ .nvptx,
+ .nvptx64,
+ => return result,
+
+ // TODO go over each item in this list and either move it to the above list, or
+ // implement the standard dynamic linker path code for it.
+ .arc,
+ .avr,
.hexagon,
.msp430,
.r600,
@@ -1256,8 +1243,6 @@ pub const Target = union(enum) {
.tce,
.tcele,
.xcore,
- .nvptx,
- .nvptx64,
.le32,
.le64,
.amdil,
@@ -1272,9 +1257,11 @@ pub const Target = union(enum) {
.renderscript32,
.renderscript64,
.ve,
- => return error.UnknownDynamicLinkerPath,
+ => return result,
},
+ // Operating systems in this list have been verified as not having a standard
+ // dynamic linker path.
.freestanding,
.ios,
.tvos,
@@ -1283,40 +1270,36 @@ pub const Target = union(enum) {
.uefi,
.windows,
.emscripten,
+ .wasi,
.other,
- => return error.TargetHasNoDynamicLinker,
+ => return result,
- else => return error.UnknownDynamicLinkerPath,
+ // TODO go over each item in this list and either move it to the above list, or
+ // implement the standard dynamic linker path code for it.
+ .ananas,
+ .cloudabi,
+ .fuchsia,
+ .kfreebsd,
+ .lv2,
+ .openbsd,
+ .solaris,
+ .haiku,
+ .minix,
+ .rtems,
+ .nacl,
+ .cnk,
+ .aix,
+ .cuda,
+ .nvcl,
+ .amdhsa,
+ .ps4,
+ .elfiamcu,
+ .mesa3d,
+ .contiki,
+ .amdpal,
+ .hermit,
+ .hurd,
+ => return result,
}
}
};
-
-test "Target.parse" {
- {
- const target = (try Target.parse(.{
- .arch_os_abi = "x86_64-linux-gnu",
- .cpu_features = "x86_64-sse-sse2-avx-cx8",
- })).Cross;
-
- std.testing.expect(target.os == .linux);
- std.testing.expect(target.abi == .gnu);
- std.testing.expect(target.cpu.arch == .x86_64);
- std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse));
- std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .avx));
- std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8));
- std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov));
- std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr));
- }
- {
- const target = (try Target.parse(.{
- .arch_os_abi = "arm-linux-musleabihf",
- .cpu_features = "generic+v8a",
- })).Cross;
-
- std.testing.expect(target.os == .linux);
- std.testing.expect(target.abi == .musleabihf);
- std.testing.expect(target.cpu.arch == .arm);
- std.testing.expect(target.cpu.model == &Target.arm.cpu.generic);
- std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a));
- }
-}
diff --git a/lib/std/target/arm.zig b/lib/std/target/arm.zig
index 6f0d7916c9..23a45851ec 100644
--- a/lib/std/target/arm.zig
+++ b/lib/std/target/arm.zig
@@ -1510,7 +1510,7 @@ pub const cpu = struct {
.name = "baseline",
.llvm_name = "generic",
.features = featureSet(&[_]Feature{
- .v6m,
+ .v7a,
}),
};
pub const cortex_a12 = CpuModel{
diff --git a/lib/std/testing.zig b/lib/std/testing.zig
index 348f651a88..4f527ba700 100644
--- a/lib/std/testing.zig
+++ b/lib/std/testing.zig
@@ -1,5 +1,3 @@
-const builtin = @import("builtin");
-const TypeId = builtin.TypeId;
const std = @import("std.zig");
pub const LeakCountAllocator = @import("testing/leak_count_allocator.zig").LeakCountAllocator;
@@ -65,16 +63,12 @@ pub fn expectEqual(expected: var, actual: @TypeOf(expected)) void {
.Pointer => |pointer| {
switch (pointer.size) {
- builtin.TypeInfo.Pointer.Size.One,
- builtin.TypeInfo.Pointer.Size.Many,
- builtin.TypeInfo.Pointer.Size.C,
- => {
+ .One, .Many, .C => {
if (actual != expected) {
std.debug.panic("expected {*}, found {*}", .{ expected, actual });
}
},
-
- builtin.TypeInfo.Pointer.Size.Slice => {
+ .Slice => {
if (actual.ptr != expected.ptr) {
std.debug.panic("expected slice ptr {}, found {}", .{ expected.ptr, actual.ptr });
}
diff --git a/lib/std/thread.zig b/lib/std/thread.zig
index fcc71ae5a5..b2f8a44a47 100644
--- a/lib/std/thread.zig
+++ b/lib/std/thread.zig
@@ -1,5 +1,5 @@
-const builtin = @import("builtin");
const std = @import("std.zig");
+const builtin = std.builtin;
const os = std.os;
const mem = std.mem;
const windows = std.os.windows;
@@ -9,14 +9,14 @@ const assert = std.debug.assert;
pub const Thread = struct {
data: Data,
- pub const use_pthreads = builtin.os != .windows and builtin.link_libc;
+ pub const use_pthreads = std.Target.current.os.tag != .windows and builtin.link_libc;
/// Represents a kernel thread handle.
/// May be an integer or a pointer depending on the platform.
/// On Linux and POSIX, this is the same as Id.
pub const Handle = if (use_pthreads)
c.pthread_t
- else switch (builtin.os) {
+ else switch (std.Target.current.os.tag) {
.linux => i32,
.windows => windows.HANDLE,
else => void,
@@ -25,7 +25,7 @@ pub const Thread = struct {
/// Represents a unique ID per thread.
/// May be an integer or pointer depending on the platform.
/// On Linux and POSIX, this is the same as Handle.
- pub const Id = switch (builtin.os) {
+ pub const Id = switch (std.Target.current.os.tag) {
.windows => windows.DWORD,
else => Handle,
};
@@ -35,7 +35,7 @@ pub const Thread = struct {
handle: Thread.Handle,
memory: []align(mem.page_size) u8,
}
- else switch (builtin.os) {
+ else switch (std.Target.current.os.tag) {
.linux => struct {
handle: Thread.Handle,
memory: []align(mem.page_size) u8,
@@ -55,7 +55,7 @@ pub const Thread = struct {
if (use_pthreads) {
return c.pthread_self();
} else
- return switch (builtin.os) {
+ return switch (std.Target.current.os.tag) {
.linux => os.linux.gettid(),
.windows => windows.kernel32.GetCurrentThreadId(),
else => @compileError("Unsupported OS"),
@@ -83,7 +83,7 @@ pub const Thread = struct {
else => unreachable,
}
os.munmap(self.data.memory);
- } else switch (builtin.os) {
+ } else switch (std.Target.current.os.tag) {
.linux => {
while (true) {
const pid_value = @atomicLoad(i32, &self.data.handle, .SeqCst);
@@ -150,7 +150,7 @@ pub const Thread = struct {
const Context = @TypeOf(context);
comptime assert(@typeInfo(@TypeOf(startFn)).Fn.args[0].arg_type.? == Context);
- if (builtin.os == builtin.Os.windows) {
+ if (std.Target.current.os.tag == .windows) {
const WinThread = struct {
const OuterContext = struct {
thread: Thread,
@@ -309,16 +309,16 @@ pub const Thread = struct {
os.EINVAL => unreachable,
else => return os.unexpectedErrno(@intCast(usize, err)),
}
- } else if (builtin.os == .linux) {
+ } else if (std.Target.current.os.tag == .linux) {
var flags: u32 = os.CLONE_VM | os.CLONE_FS | os.CLONE_FILES | os.CLONE_SIGHAND |
os.CLONE_THREAD | os.CLONE_SYSVSEM | os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID |
os.CLONE_DETACHED;
var newtls: usize = undefined;
// This structure is only needed when targeting i386
- var user_desc: if (builtin.arch == .i386) os.linux.user_desc else void = undefined;
+ var user_desc: if (std.Target.current.cpu.arch == .i386) os.linux.user_desc else void = undefined;
if (os.linux.tls.tls_image) |tls_img| {
- if (builtin.arch == .i386) {
+ if (std.Target.current.cpu.arch == .i386) {
user_desc = os.linux.user_desc{
.entry_number = tls_img.gdt_entry_number,
.base_addr = os.linux.tls.copyTLS(mmap_addr + tls_start_offset),
@@ -362,27 +362,24 @@ pub const Thread = struct {
}
pub const CpuCountError = error{
- OutOfMemory,
PermissionDenied,
SystemResources,
Unexpected,
};
pub fn cpuCount() CpuCountError!usize {
- if (builtin.os == .linux) {
+ if (std.Target.current.os.tag == .linux) {
const cpu_set = try os.sched_getaffinity(0);
return @as(usize, os.CPU_COUNT(cpu_set)); // TODO should not need this usize cast
}
- if (builtin.os == .windows) {
- var system_info: windows.SYSTEM_INFO = undefined;
- windows.kernel32.GetSystemInfo(&system_info);
- return @intCast(usize, system_info.dwNumberOfProcessors);
+ if (std.Target.current.os.tag == .windows) {
+ return os.windows.peb().NumberOfProcessors;
}
var count: c_int = undefined;
var count_len: usize = @sizeOf(c_int);
const name = if (comptime std.Target.current.isDarwin()) "hw.logicalcpu" else "hw.ncpu";
os.sysctlbynameC(name, &count, &count_len, null, 0) catch |err| switch (err) {
- error.NameTooLong => unreachable,
+ error.NameTooLong, error.UnknownName => unreachable,
else => |e| return e,
};
return @intCast(usize, count);
diff --git a/lib/std/time.zig b/lib/std/time.zig
index 63d3ecce18..4112fb7bda 100644
--- a/lib/std/time.zig
+++ b/lib/std/time.zig
@@ -1,5 +1,5 @@
-const builtin = @import("builtin");
const std = @import("std.zig");
+const builtin = std.builtin;
const assert = std.debug.assert;
const testing = std.testing;
const os = std.os;
@@ -7,10 +7,12 @@ const math = std.math;
pub const epoch = @import("time/epoch.zig");
+const is_windows = std.Target.current.os.tag == .windows;
+
/// Spurious wakeups are possible and no precision of timing is guaranteed.
/// TODO integrate with evented I/O
pub fn sleep(nanoseconds: u64) void {
- if (builtin.os == .windows) {
+ if (is_windows) {
const ns_per_ms = ns_per_s / ms_per_s;
const big_ms_from_ns = nanoseconds / ns_per_ms;
const ms = math.cast(os.windows.DWORD, big_ms_from_ns) catch math.maxInt(os.windows.DWORD);
@@ -31,7 +33,7 @@ pub fn timestamp() u64 {
/// Get the posix timestamp, UTC, in milliseconds
/// TODO audit this function. is it possible to return an error?
pub fn milliTimestamp() u64 {
- if (builtin.os == .windows) {
+ if (is_windows) {
//FileTime has a granularity of 100 nanoseconds
// and uses the NTFS/Windows epoch
var ft: os.windows.FILETIME = undefined;
@@ -42,7 +44,7 @@ pub fn milliTimestamp() u64 {
const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
return @divFloor(ft64, hns_per_ms) - -epoch_adj;
}
- if (builtin.os == .wasi and !builtin.link_libc) {
+ if (builtin.os.tag == .wasi and !builtin.link_libc) {
var ns: os.wasi.timestamp_t = undefined;
// TODO: Verify that precision is ignored
@@ -102,7 +104,7 @@ pub const Timer = struct {
///if we used resolution's value when performing the
/// performance counter calc on windows/darwin, it would
/// be less precise
- frequency: switch (builtin.os) {
+ frequency: switch (builtin.os.tag) {
.windows => u64,
.macosx, .ios, .tvos, .watchos => os.darwin.mach_timebase_info_data,
else => void,
@@ -127,7 +129,7 @@ pub const Timer = struct {
pub fn start() Error!Timer {
var self: Timer = undefined;
- if (builtin.os == .windows) {
+ if (is_windows) {
self.frequency = os.windows.QueryPerformanceFrequency();
self.resolution = @divFloor(ns_per_s, self.frequency);
self.start_time = os.windows.QueryPerformanceCounter();
@@ -172,7 +174,7 @@ pub const Timer = struct {
}
fn clockNative() u64 {
- if (builtin.os == .windows) {
+ if (is_windows) {
return os.windows.QueryPerformanceCounter();
}
if (comptime std.Target.current.isDarwin()) {
@@ -184,7 +186,7 @@ pub const Timer = struct {
}
fn nativeDurationToNanos(self: Timer, duration: u64) u64 {
- if (builtin.os == .windows) {
+ if (is_windows) {
return @divFloor(duration * ns_per_s, self.frequency);
}
if (comptime std.Target.current.isDarwin()) {
diff --git a/lib/std/valgrind.zig b/lib/std/valgrind.zig
index fe1eb65b7a..38c4a491e0 100644
--- a/lib/std/valgrind.zig
+++ b/lib/std/valgrind.zig
@@ -8,7 +8,7 @@ pub fn doClientRequest(default: usize, request: usize, a1: usize, a2: usize, a3:
}
switch (builtin.arch) {
- builtin.Arch.i386 => {
+ .i386 => {
return asm volatile (
\\ roll $3, %%edi ; roll $13, %%edi
\\ roll $29, %%edi ; roll $19, %%edi
@@ -19,7 +19,7 @@ pub fn doClientRequest(default: usize, request: usize, a1: usize, a2: usize, a3:
: "cc", "memory"
);
},
- builtin.Arch.x86_64 => {
+ .x86_64 => {
return asm volatile (
\\ rolq $3, %%rdi ; rolq $13, %%rdi
\\ rolq $61, %%rdi ; rolq $51, %%rdi
diff --git a/lib/std/zig.zig b/lib/std/zig.zig
index d76ed9dfd2..81f34b09c9 100644
--- a/lib/std/zig.zig
+++ b/lib/std/zig.zig
@@ -6,11 +6,8 @@ pub const parseStringLiteral = @import("zig/parse_string_literal.zig").parseStri
pub const render = @import("zig/render.zig").render;
pub const ast = @import("zig/ast.zig");
pub const system = @import("zig/system.zig");
+pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
-test "std.zig tests" {
- _ = @import("zig/ast.zig");
- _ = @import("zig/parse.zig");
- _ = @import("zig/render.zig");
- _ = @import("zig/tokenizer.zig");
- _ = @import("zig/parse_string_literal.zig");
+test "" {
+ @import("std").meta.refAllDecls(@This());
}
diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig
new file mode 100644
index 0000000000..38ce6549f3
--- /dev/null
+++ b/lib/std/zig/cross_target.zig
@@ -0,0 +1,839 @@
+const std = @import("../std.zig");
+const assert = std.debug.assert;
+const Target = std.Target;
+const mem = std.mem;
+
+/// Contains all the same data as `Target`, additionally introducing the concept of "the native target".
+/// The purpose of this abstraction is to provide meaningful and unsurprising defaults.
+/// This struct does reference any resources and it is copyable.
+pub const CrossTarget = struct {
+ /// `null` means native.
+ cpu_arch: ?Target.Cpu.Arch = null,
+
+ cpu_model: CpuModel = CpuModel.determined_by_cpu_arch,
+
+ /// Sparse set of CPU features to add to the set from `cpu_model`.
+ cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
+
+ /// Sparse set of CPU features to remove from the set from `cpu_model`.
+ cpu_features_sub: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
+
+ /// `null` means native.
+ os_tag: ?Target.Os.Tag = null,
+
+ /// `null` means the default version range for `os_tag`. If `os_tag` is `null` (native)
+ /// then `null` for this field means native.
+ os_version_min: ?OsVersion = null,
+
+ /// When cross compiling, `null` means default (latest known OS version).
+ /// When `os_tag` is native, `null` means equal to the native OS version.
+ os_version_max: ?OsVersion = null,
+
+ /// `null` means default when cross compiling, or native when os_tag is native.
+ /// If `isGnuLibC()` is `false`, this must be `null` and is ignored.
+ glibc_version: ?SemVer = null,
+
+ /// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI.
+ abi: ?Target.Abi = null,
+
+ /// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path
+ /// based on the `os_tag`.
+ dynamic_linker: DynamicLinker = DynamicLinker{},
+
+ pub const CpuModel = union(enum) {
+ /// Always native
+ native,
+
+ /// Always baseline
+ baseline,
+
+ /// If CPU Architecture is native, then the CPU model will be native. Otherwise,
+ /// it will be baseline.
+ determined_by_cpu_arch,
+
+ explicit: *const Target.Cpu.Model,
+ };
+
+ pub const OsVersion = union(enum) {
+ none: void,
+ semver: SemVer,
+ windows: Target.Os.WindowsVersion,
+ };
+
+ pub const SemVer = std.builtin.Version;
+
+ pub const DynamicLinker = Target.DynamicLinker;
+
+ pub fn fromTarget(target: Target) CrossTarget {
+ var result: CrossTarget = .{
+ .cpu_arch = target.cpu.arch,
+ .cpu_model = .{ .explicit = target.cpu.model },
+ .os_tag = target.os.tag,
+ .os_version_min = undefined,
+ .os_version_max = undefined,
+ .abi = target.abi,
+ .glibc_version = if (target.isGnuLibC())
+ target.os.version_range.linux.glibc
+ else
+ null,
+ };
+ result.updateOsVersionRange(target.os);
+
+ const all_features = target.cpu.arch.allFeaturesList();
+ var cpu_model_set = target.cpu.model.features;
+ cpu_model_set.populateDependencies(all_features);
+ {
+ // The "add" set is the full set with the CPU Model set removed.
+ const add_set = &result.cpu_features_add;
+ add_set.* = target.cpu.features;
+ add_set.removeFeatureSet(cpu_model_set);
+ }
+ {
+ // The "sub" set is the features that are on in CPU Model set and off in the full set.
+ const sub_set = &result.cpu_features_sub;
+ sub_set.* = cpu_model_set;
+ sub_set.removeFeatureSet(target.cpu.features);
+ }
+ return result;
+ }
+
+ fn updateOsVersionRange(self: *CrossTarget, os: Target.Os) void {
+ switch (os.tag) {
+ .freestanding,
+ .ananas,
+ .cloudabi,
+ .dragonfly,
+ .fuchsia,
+ .kfreebsd,
+ .lv2,
+ .solaris,
+ .haiku,
+ .minix,
+ .rtems,
+ .nacl,
+ .cnk,
+ .aix,
+ .cuda,
+ .nvcl,
+ .amdhsa,
+ .ps4,
+ .elfiamcu,
+ .mesa3d,
+ .contiki,
+ .amdpal,
+ .hermit,
+ .hurd,
+ .wasi,
+ .emscripten,
+ .uefi,
+ .other,
+ => {
+ self.os_version_min = .{ .none = {} };
+ self.os_version_max = .{ .none = {} };
+ },
+
+ .freebsd,
+ .macosx,
+ .ios,
+ .netbsd,
+ .openbsd,
+ .tvos,
+ .watchos,
+ => {
+ self.os_version_min = .{ .semver = os.version_range.semver.min };
+ self.os_version_max = .{ .semver = os.version_range.semver.max };
+ },
+
+ .linux => {
+ self.os_version_min = .{ .semver = os.version_range.linux.range.min };
+ self.os_version_max = .{ .semver = os.version_range.linux.range.max };
+ },
+
+ .windows => {
+ self.os_version_min = .{ .windows = os.version_range.windows.min };
+ self.os_version_max = .{ .windows = os.version_range.windows.max };
+ },
+ }
+ }
+
+ /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
+ pub fn toTarget(self: CrossTarget) Target {
+ return .{
+ .cpu = self.getCpu(),
+ .os = self.getOs(),
+ .abi = self.getAbi(),
+ };
+ }
+
+ pub const ParseOptions = struct {
+ /// This is sometimes called a "triple". It looks roughly like this:
+ /// riscv64-linux-musl
+ /// The fields are, respectively:
+ /// * CPU Architecture
+ /// * Operating System (and optional version range)
+ /// * C ABI (optional, with optional glibc version)
+ /// The string "native" can be used for CPU architecture as well as Operating System.
+ /// If the CPU Architecture is specified as "native", then the Operating System and C ABI may be omitted.
+ arch_os_abi: []const u8 = "native",
+
+ /// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e"
+ /// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features
+ /// to remove from the set.
+ /// The following special strings are recognized for CPU Model name:
+ /// * "baseline" - The "default" set of CPU features for cross-compiling. A conservative set
+ /// of features that is expected to be supported on most available hardware.
+ /// * "native" - The native CPU model is to be detected when compiling.
+ /// If this field is not provided (`null`), then the value will depend on the
+ /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline".
+ cpu_features: ?[]const u8 = null,
+
+ /// Absolute path to dynamic linker, to override the default, which is either a natively
+ /// detected path, or a standard path.
+ dynamic_linker: ?[]const u8 = null,
+
+ /// If this is provided, the function will populate some information about parsing failures,
+ /// so that user-friendly error messages can be delivered.
+ diagnostics: ?*Diagnostics = null,
+
+ pub const Diagnostics = struct {
+ /// If the architecture was determined, this will be populated.
+ arch: ?Target.Cpu.Arch = null,
+
+ /// If the OS name was determined, this will be populated.
+ os_name: ?[]const u8 = null,
+
+ /// If the OS tag was determined, this will be populated.
+ os_tag: ?Target.Os.Tag = null,
+
+ /// If the ABI was determined, this will be populated.
+ abi: ?Target.Abi = null,
+
+ /// If the CPU name was determined, this will be populated.
+ cpu_name: ?[]const u8 = null,
+
+ /// If error.UnknownCpuFeature is returned, this will be populated.
+ unknown_feature_name: ?[]const u8 = null,
+ };
+ };
+
+ pub fn parse(args: ParseOptions) !CrossTarget {
+ var dummy_diags: ParseOptions.Diagnostics = undefined;
+ const diags = args.diagnostics orelse &dummy_diags;
+
+ var result: CrossTarget = .{
+ .dynamic_linker = DynamicLinker.init(args.dynamic_linker),
+ };
+
+ var it = mem.separate(args.arch_os_abi, "-");
+ const arch_name = it.next().?;
+ const arch_is_native = mem.eql(u8, arch_name, "native");
+ if (!arch_is_native) {
+ result.cpu_arch = std.meta.stringToEnum(Target.Cpu.Arch, arch_name) orelse
+ return error.UnknownArchitecture;
+ }
+ const arch = result.getCpuArch();
+ diags.arch = arch;
+
+ if (it.next()) |os_text| {
+ try parseOs(&result, diags, os_text);
+ } else if (!arch_is_native) {
+ return error.MissingOperatingSystem;
+ }
+
+ const opt_abi_text = it.next();
+ if (opt_abi_text) |abi_text| {
+ var abi_it = mem.separate(abi_text, ".");
+ const abi = std.meta.stringToEnum(Target.Abi, abi_it.next().?) orelse
+ return error.UnknownApplicationBinaryInterface;
+ result.abi = abi;
+ diags.abi = abi;
+
+ const abi_ver_text = abi_it.rest();
+ if (abi_it.next() != null) {
+ if (result.isGnuLibC()) {
+ result.glibc_version = SemVer.parse(abi_ver_text) catch |err| switch (err) {
+ error.Overflow => return error.InvalidAbiVersion,
+ error.InvalidCharacter => return error.InvalidAbiVersion,
+ error.InvalidVersion => return error.InvalidAbiVersion,
+ };
+ } else {
+ return error.InvalidAbiVersion;
+ }
+ }
+ }
+
+ if (it.next() != null) return error.UnexpectedExtraField;
+
+ if (args.cpu_features) |cpu_features| {
+ const all_features = arch.allFeaturesList();
+ var index: usize = 0;
+ while (index < cpu_features.len and
+ cpu_features[index] != '+' and
+ cpu_features[index] != '-')
+ {
+ index += 1;
+ }
+ const cpu_name = cpu_features[0..index];
+ diags.cpu_name = cpu_name;
+
+ const add_set = &result.cpu_features_add;
+ const sub_set = &result.cpu_features_sub;
+ if (mem.eql(u8, cpu_name, "native")) {
+ result.cpu_model = .native;
+ } else if (mem.eql(u8, cpu_name, "baseline")) {
+ result.cpu_model = .baseline;
+ } else {
+ result.cpu_model = .{ .explicit = try arch.parseCpuModel(cpu_name) };
+ }
+
+ while (index < cpu_features.len) {
+ const op = cpu_features[index];
+ const set = switch (op) {
+ '+' => add_set,
+ '-' => sub_set,
+ else => unreachable,
+ };
+ index += 1;
+ const start = index;
+ while (index < cpu_features.len and
+ cpu_features[index] != '+' and
+ cpu_features[index] != '-')
+ {
+ index += 1;
+ }
+ const feature_name = cpu_features[start..index];
+ for (all_features) |feature, feat_index_usize| {
+ const feat_index = @intCast(Target.Cpu.Feature.Set.Index, feat_index_usize);
+ if (mem.eql(u8, feature_name, feature.name)) {
+ set.addFeature(feat_index);
+ break;
+ }
+ } else {
+ diags.unknown_feature_name = feature_name;
+ return error.UnknownCpuFeature;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
+ pub fn getCpu(self: CrossTarget) Target.Cpu {
+ switch (self.cpu_model) {
+ .native => {
+ // This works when doing `zig build` because Zig generates a build executable using
+ // native CPU model & features. However this will not be accurate otherwise, and
+ // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
+ return Target.current.cpu;
+ },
+ .baseline => {
+ var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch());
+ self.updateCpuFeatures(&adjusted_baseline.features);
+ return adjusted_baseline;
+ },
+ .determined_by_cpu_arch => if (self.cpu_arch == null) {
+ // This works when doing `zig build` because Zig generates a build executable using
+ // native CPU model & features. However this will not be accurate otherwise, and
+ // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
+ return Target.current.cpu;
+ } else {
+ var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch());
+ self.updateCpuFeatures(&adjusted_baseline.features);
+ return adjusted_baseline;
+ },
+ .explicit => |model| {
+ var adjusted_model = model.toCpu(self.getCpuArch());
+ self.updateCpuFeatures(&adjusted_model.features);
+ return adjusted_model;
+ },
+ }
+ }
+
+ pub fn getCpuArch(self: CrossTarget) Target.Cpu.Arch {
+ return self.cpu_arch orelse Target.current.cpu.arch;
+ }
+
+ pub fn getCpuModel(self: CrossTarget) *const Target.Cpu.Model {
+ if (self.cpu_model) |cpu_model| return cpu_model;
+ return self.getCpu().model;
+ }
+
+ pub fn getCpuFeatures(self: CrossTarget) Target.Cpu.Feature.Set {
+ return self.getCpu().features;
+ }
+
+ /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
+ pub fn getOs(self: CrossTarget) Target.Os {
+ // `Target.current.os` works when doing `zig build` because Zig generates a build executable using
+ // native OS version range. However this will not be accurate otherwise, and
+ // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
+ var adjusted_os = if (self.os_tag) |os_tag| Target.Os.defaultVersionRange(os_tag) else Target.current.os;
+
+ if (self.os_version_min) |min| switch (min) {
+ .none => {},
+ .semver => |semver| switch (self.getOsTag()) {
+ .linux => adjusted_os.version_range.linux.range.min = semver,
+ else => adjusted_os.version_range.semver.min = semver,
+ },
+ .windows => |win_ver| adjusted_os.version_range.windows.min = win_ver,
+ };
+
+ if (self.os_version_max) |max| switch (max) {
+ .none => {},
+ .semver => |semver| switch (self.getOsTag()) {
+ .linux => adjusted_os.version_range.linux.range.max = semver,
+ else => adjusted_os.version_range.semver.max = semver,
+ },
+ .windows => |win_ver| adjusted_os.version_range.windows.max = win_ver,
+ };
+
+ if (self.glibc_version) |glibc| {
+ assert(self.isGnuLibC());
+ adjusted_os.version_range.linux.glibc = glibc;
+ }
+
+ return adjusted_os;
+ }
+
+ pub fn getOsTag(self: CrossTarget) Target.Os.Tag {
+ return self.os_tag orelse Target.current.os.tag;
+ }
+
+ /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
+ pub fn getOsVersionMin(self: CrossTarget) OsVersion {
+ if (self.os_version_min) |version_min| return version_min;
+ var tmp: CrossTarget = undefined;
+ tmp.updateOsVersionRange(self.getOs());
+ return tmp.os_version_min.?;
+ }
+
+ /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
+ pub fn getOsVersionMax(self: CrossTarget) OsVersion {
+ if (self.os_version_max) |version_max| return version_max;
+ var tmp: CrossTarget = undefined;
+ tmp.updateOsVersionRange(self.getOs());
+ return tmp.os_version_max.?;
+ }
+
+ /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
+ pub fn getAbi(self: CrossTarget) Target.Abi {
+ if (self.abi) |abi| return abi;
+
+ if (self.os_tag == null) {
+ // This works when doing `zig build` because Zig generates a build executable using
+ // native CPU model & features. However this will not be accurate otherwise, and
+ // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
+ return Target.current.abi;
+ }
+
+ return Target.Abi.default(self.getCpuArch(), self.getOs());
+ }
+
+ pub fn isFreeBSD(self: CrossTarget) bool {
+ return self.getOsTag() == .freebsd;
+ }
+
+ pub fn isDarwin(self: CrossTarget) bool {
+ return self.getOsTag().isDarwin();
+ }
+
+ pub fn isNetBSD(self: CrossTarget) bool {
+ return self.getOsTag() == .netbsd;
+ }
+
+ pub fn isUefi(self: CrossTarget) bool {
+ return self.getOsTag() == .uefi;
+ }
+
+ pub fn isDragonFlyBSD(self: CrossTarget) bool {
+ return self.getOsTag() == .dragonfly;
+ }
+
+ pub fn isLinux(self: CrossTarget) bool {
+ return self.getOsTag() == .linux;
+ }
+
+ pub fn isWindows(self: CrossTarget) bool {
+ return self.getOsTag() == .windows;
+ }
+
+ pub fn oFileExt(self: CrossTarget) [:0]const u8 {
+ return self.getAbi().oFileExt();
+ }
+
+ pub fn exeFileExt(self: CrossTarget) [:0]const u8 {
+ return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag());
+ }
+
+ pub fn staticLibSuffix(self: CrossTarget) [:0]const u8 {
+ return Target.staticLibSuffix_cpu_arch_abi(self.getCpuArch(), self.getAbi());
+ }
+
+ pub fn dynamicLibSuffix(self: CrossTarget) [:0]const u8 {
+ return self.getOsTag().dynamicLibSuffix();
+ }
+
+ pub fn libPrefix(self: CrossTarget) [:0]const u8 {
+ return Target.libPrefix_cpu_arch_abi(self.getCpuArch(), self.getAbi());
+ }
+
+ pub fn isNative(self: CrossTarget) bool {
+ return self.cpu_arch == null and
+ (self.cpu_model == .native or self.cpu_model == .determined_by_cpu_arch) and
+ self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty() and
+ self.os_tag == null and self.os_version_min == null and self.os_version_max == null and
+ self.abi == null and self.dynamic_linker.get() == null and self.glibc_version == null;
+ }
+
+ pub fn zigTriple(self: CrossTarget, allocator: *mem.Allocator) error{OutOfMemory}![:0]u8 {
+ if (self.isNative()) {
+ return mem.dupeZ(allocator, u8, "native");
+ }
+
+ const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native";
+ const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native";
+
+ var result = try std.Buffer.allocPrint(allocator, "{}-{}", .{ arch_name, os_name });
+ defer result.deinit();
+
+ // The zig target syntax does not allow specifying a max os version with no min, so
+ // if either are present, we need the min.
+ if (self.os_version_min != null or self.os_version_max != null) {
+ switch (self.getOsVersionMin()) {
+ .none => {},
+ .semver => |v| try result.print(".{}", .{v}),
+ .windows => |v| try result.print(".{}", .{@tagName(v)}),
+ }
+ }
+ if (self.os_version_max) |max| {
+ switch (max) {
+ .none => {},
+ .semver => |v| try result.print("...{}", .{v}),
+ .windows => |v| try result.print("...{}", .{@tagName(v)}),
+ }
+ }
+
+ if (self.glibc_version) |v| {
+ try result.print("-{}.{}", .{ @tagName(self.getAbi()), v });
+ } else if (self.abi) |abi| {
+ try result.print("-{}", .{@tagName(abi)});
+ }
+
+ return result.toOwnedSlice();
+ }
+
+ pub fn allocDescription(self: CrossTarget, allocator: *mem.Allocator) ![:0]u8 {
+ // TODO is there anything else worthy of the description that is not
+ // already captured in the triple?
+ return self.zigTriple(allocator);
+ }
+
+ pub fn linuxTriple(self: CrossTarget, allocator: *mem.Allocator) ![:0]u8 {
+ return Target.linuxTripleSimple(allocator, self.getCpuArch(), self.getOsTag(), self.getAbi());
+ }
+
+ pub fn wantSharedLibSymLinks(self: CrossTarget) bool {
+ return self.getOsTag() != .windows;
+ }
+
+ pub const VcpkgLinkage = std.builtin.LinkMode;
+
+ /// Returned slice must be freed by the caller.
+ pub fn vcpkgTriplet(self: CrossTarget, allocator: *mem.Allocator, linkage: VcpkgLinkage) ![:0]u8 {
+ const arch = switch (self.getCpuArch()) {
+ .i386 => "x86",
+ .x86_64 => "x64",
+
+ .arm,
+ .armeb,
+ .thumb,
+ .thumbeb,
+ .aarch64_32,
+ => "arm",
+
+ .aarch64,
+ .aarch64_be,
+ => "arm64",
+
+ else => return error.UnsupportedVcpkgArchitecture,
+ };
+
+ const os = switch (self.getOsTag()) {
+ .windows => "windows",
+ .linux => "linux",
+ .macosx => "macos",
+ else => return error.UnsupportedVcpkgOperatingSystem,
+ };
+
+ const static_suffix = switch (linkage) {
+ .Static => "-static",
+ .Dynamic => "",
+ };
+
+ return std.fmt.allocPrint0(allocator, "{}-{}{}", .{ arch, os, static_suffix });
+ }
+
+ pub const Executor = union(enum) {
+ native,
+ qemu: []const u8,
+ wine: []const u8,
+ wasmtime: []const u8,
+ unavailable,
+ };
+
+ /// Note that even a `CrossTarget` which returns `false` for `isNative` could still be natively executed.
+ /// For example `-target arm-native` running on an aarch64 host.
+ pub fn getExternalExecutor(self: CrossTarget) Executor {
+ const cpu_arch = self.getCpuArch();
+ const os_tag = self.getOsTag();
+ const os_match = os_tag == Target.current.os.tag;
+
+ // If the OS and CPU arch match, the binary can be considered native.
+ if (os_match and cpu_arch == Target.current.cpu.arch) {
+ // However, we also need to verify that the dynamic linker path is valid.
+ // TODO Until that is implemented, we prevent returning `.native` when the OS is non-native.
+ if (self.os_tag == null) {
+ return .native;
+ }
+ }
+
+ // If the OS matches, we can use QEMU to emulate a foreign architecture.
+ if (os_match) {
+ return switch (cpu_arch) {
+ .aarch64 => Executor{ .qemu = "qemu-aarch64" },
+ .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" },
+ .arm => Executor{ .qemu = "qemu-arm" },
+ .armeb => Executor{ .qemu = "qemu-armeb" },
+ .i386 => Executor{ .qemu = "qemu-i386" },
+ .mips => Executor{ .qemu = "qemu-mips" },
+ .mipsel => Executor{ .qemu = "qemu-mipsel" },
+ .mips64 => Executor{ .qemu = "qemu-mips64" },
+ .mips64el => Executor{ .qemu = "qemu-mips64el" },
+ .powerpc => Executor{ .qemu = "qemu-ppc" },
+ .powerpc64 => Executor{ .qemu = "qemu-ppc64" },
+ .powerpc64le => Executor{ .qemu = "qemu-ppc64le" },
+ .riscv32 => Executor{ .qemu = "qemu-riscv32" },
+ .riscv64 => Executor{ .qemu = "qemu-riscv64" },
+ .s390x => Executor{ .qemu = "qemu-s390x" },
+ .sparc => Executor{ .qemu = "qemu-sparc" },
+ .x86_64 => Executor{ .qemu = "qemu-x86_64" },
+ else => return .unavailable,
+ };
+ }
+
+ switch (os_tag) {
+ .windows => switch (cpu_arch.ptrBitWidth()) {
+ 32 => return Executor{ .wine = "wine" },
+ 64 => return Executor{ .wine = "wine64" },
+ else => return .unavailable,
+ },
+ .wasi => switch (cpu_arch.ptrBitWidth()) {
+ 32 => return Executor{ .wasmtime = "wasmtime" },
+ else => return .unavailable,
+ },
+ else => return .unavailable,
+ }
+ }
+
+ pub fn isGnuLibC(self: CrossTarget) bool {
+ return Target.isGnuLibC_os_tag_abi(self.getOsTag(), self.getAbi());
+ }
+
+ pub fn setGnuLibCVersion(self: *CrossTarget, major: u32, minor: u32, patch: u32) void {
+ assert(self.isGnuLibC());
+ self.glibc_version = SemVer{ .major = major, .minor = minor, .patch = patch };
+ }
+
+ pub fn getObjectFormat(self: CrossTarget) ObjectFormat {
+ return Target.getObjectFormatSimple(self.getOsTag(), self.getCpuArch());
+ }
+
+ fn updateCpuFeatures(self: CrossTarget, set: *Target.Cpu.Feature.Set) void {
+ set.removeFeatureSet(self.cpu_features_sub);
+ set.addFeatureSet(self.cpu_features_add);
+ set.populateDependencies(self.getCpuArch().allFeaturesList());
+ set.removeFeatureSet(self.cpu_features_sub);
+ }
+
+ fn parseOs(result: *CrossTarget, diags: *ParseOptions.Diagnostics, text: []const u8) !void {
+ var it = mem.separate(text, ".");
+ const os_name = it.next().?;
+ diags.os_name = os_name;
+ const os_is_native = mem.eql(u8, os_name, "native");
+ if (!os_is_native) {
+ result.os_tag = std.meta.stringToEnum(Target.Os.Tag, os_name) orelse
+ return error.UnknownOperatingSystem;
+ }
+ const tag = result.getOsTag();
+ diags.os_tag = tag;
+
+ const version_text = it.rest();
+ if (it.next() == null) return;
+
+ switch (tag) {
+ .freestanding,
+ .ananas,
+ .cloudabi,
+ .dragonfly,
+ .fuchsia,
+ .ios,
+ .kfreebsd,
+ .lv2,
+ .solaris,
+ .haiku,
+ .minix,
+ .rtems,
+ .nacl,
+ .cnk,
+ .aix,
+ .cuda,
+ .nvcl,
+ .amdhsa,
+ .ps4,
+ .elfiamcu,
+ .tvos,
+ .watchos,
+ .mesa3d,
+ .contiki,
+ .amdpal,
+ .hermit,
+ .hurd,
+ .wasi,
+ .emscripten,
+ .uefi,
+ .other,
+ => return error.InvalidOperatingSystemVersion,
+
+ .freebsd,
+ .macosx,
+ .netbsd,
+ .openbsd,
+ .linux,
+ => {
+ var range_it = mem.separate(version_text, "...");
+
+ const min_text = range_it.next().?;
+ const min_ver = SemVer.parse(min_text) catch |err| switch (err) {
+ error.Overflow => return error.InvalidOperatingSystemVersion,
+ error.InvalidCharacter => return error.InvalidOperatingSystemVersion,
+ error.InvalidVersion => return error.InvalidOperatingSystemVersion,
+ };
+ result.os_version_min = .{ .semver = min_ver };
+
+ const max_text = range_it.next() orelse return;
+ const max_ver = SemVer.parse(max_text) catch |err| switch (err) {
+ error.Overflow => return error.InvalidOperatingSystemVersion,
+ error.InvalidCharacter => return error.InvalidOperatingSystemVersion,
+ error.InvalidVersion => return error.InvalidOperatingSystemVersion,
+ };
+ result.os_version_max = .{ .semver = max_ver };
+ },
+
+ .windows => {
+ var range_it = mem.separate(version_text, "...");
+
+ const min_text = range_it.next().?;
+ const min_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, min_text) orelse
+ return error.InvalidOperatingSystemVersion;
+ result.os_version_min = .{ .windows = min_ver };
+
+ const max_text = range_it.next() orelse return;
+ const max_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, max_text) orelse
+ return error.InvalidOperatingSystemVersion;
+ result.os_version_max = .{ .windows = max_ver };
+ },
+ }
+ }
+};
+
+test "CrossTarget.parse" {
+ if (Target.current.isGnuLibC()) {
+ var cross_target = try CrossTarget.parse(.{});
+ cross_target.setGnuLibCVersion(2, 1, 1);
+
+ const text = try cross_target.zigTriple(std.testing.allocator);
+ defer std.testing.allocator.free(text);
+ std.testing.expectEqualSlices(u8, "native-native-gnu.2.1.1", text);
+ }
+ {
+ const cross_target = try CrossTarget.parse(.{
+ .arch_os_abi = "aarch64-linux",
+ .cpu_features = "native",
+ });
+
+ std.testing.expect(cross_target.cpu_arch.? == .aarch64);
+ std.testing.expect(cross_target.cpu_model == .native);
+ }
+ {
+ const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "native" });
+
+ std.testing.expect(cross_target.cpu_arch == null);
+ std.testing.expect(cross_target.isNative());
+
+ const text = try cross_target.zigTriple(std.testing.allocator);
+ defer std.testing.allocator.free(text);
+ std.testing.expectEqualSlices(u8, "native", text);
+ }
+ {
+ const cross_target = try CrossTarget.parse(.{
+ .arch_os_abi = "x86_64-linux-gnu",
+ .cpu_features = "x86_64-sse-sse2-avx-cx8",
+ });
+ const target = cross_target.toTarget();
+
+ std.testing.expect(target.os.tag == .linux);
+ std.testing.expect(target.abi == .gnu);
+ std.testing.expect(target.cpu.arch == .x86_64);
+ std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse));
+ std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .avx));
+ std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8));
+ std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov));
+ std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr));
+
+ const text = try cross_target.zigTriple(std.testing.allocator);
+ defer std.testing.allocator.free(text);
+ std.testing.expectEqualSlices(u8, "x86_64-linux-gnu", text);
+ }
+ {
+ const cross_target = try CrossTarget.parse(.{
+ .arch_os_abi = "arm-linux-musleabihf",
+ .cpu_features = "generic+v8a",
+ });
+ const target = cross_target.toTarget();
+
+ std.testing.expect(target.os.tag == .linux);
+ std.testing.expect(target.abi == .musleabihf);
+ std.testing.expect(target.cpu.arch == .arm);
+ std.testing.expect(target.cpu.model == &Target.arm.cpu.generic);
+ std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a));
+
+ const text = try cross_target.zigTriple(std.testing.allocator);
+ defer std.testing.allocator.free(text);
+ std.testing.expectEqualSlices(u8, "arm-linux-musleabihf", text);
+ }
+ {
+ const cross_target = try CrossTarget.parse(.{
+ .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27",
+ .cpu_features = "generic+v8a",
+ });
+ const target = cross_target.toTarget();
+
+ std.testing.expect(target.cpu.arch == .aarch64);
+ std.testing.expect(target.os.tag == .linux);
+ std.testing.expect(target.os.version_range.linux.range.min.major == 3);
+ std.testing.expect(target.os.version_range.linux.range.min.minor == 10);
+ std.testing.expect(target.os.version_range.linux.range.min.patch == 0);
+ std.testing.expect(target.os.version_range.linux.range.max.major == 4);
+ std.testing.expect(target.os.version_range.linux.range.max.minor == 4);
+ std.testing.expect(target.os.version_range.linux.range.max.patch == 1);
+ std.testing.expect(target.os.version_range.linux.glibc.major == 2);
+ std.testing.expect(target.os.version_range.linux.glibc.minor == 27);
+ std.testing.expect(target.os.version_range.linux.glibc.patch == 0);
+ std.testing.expect(target.abi == .gnu);
+
+ const text = try cross_target.zigTriple(std.testing.allocator);
+ defer std.testing.allocator.free(text);
+ std.testing.expectEqualSlices(u8, "aarch64-linux.3.10...4.4.1-gnu.2.27", text);
+ }
+}
diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig
index 3931e362c6..38a90f4c60 100644
--- a/lib/std/zig/system.zig
+++ b/lib/std/zig/system.zig
@@ -1,11 +1,15 @@
const std = @import("../std.zig");
+const elf = std.elf;
const mem = std.mem;
+const fs = std.fs;
const Allocator = std.mem.Allocator;
const ArrayList = std.ArrayList;
const assert = std.debug.assert;
const process = std.process;
+const Target = std.Target;
+const CrossTarget = std.zig.CrossTarget;
-const is_windows = std.Target.current.isWindows();
+const is_windows = Target.current.os.tag == .windows;
pub const NativePaths = struct {
include_dirs: ArrayList([:0]u8),
@@ -77,7 +81,7 @@ pub const NativePaths = struct {
}
if (!is_windows) {
- const triple = try std.Target.current.linuxTriple(allocator);
+ const triple = try Target.current.linuxTriple(allocator);
// TODO: $ ld --verbose | grep SEARCH_DIR
// the output contains some paths that end with lib64, maybe include them too?
@@ -161,3 +165,691 @@ pub const NativePaths = struct {
try array.append(item);
}
};
+
+pub const NativeTargetInfo = struct {
+ target: Target,
+
+ dynamic_linker: DynamicLinker = DynamicLinker{},
+
+ pub const DynamicLinker = Target.DynamicLinker;
+
+ pub const DetectError = error{
+ OutOfMemory,
+ FileSystem,
+ SystemResources,
+ SymLinkLoop,
+ ProcessFdQuotaExceeded,
+ SystemFdQuotaExceeded,
+ DeviceBusy,
+ };
+
+ /// Given a `CrossTarget`, which specifies in detail which parts of the target should be detected
+ /// natively, which should be standard or default, and which are provided explicitly, this function
+ /// resolves the native components by detecting the native system, and then resolves standard/default parts
+ /// relative to that.
+ /// Any resources this function allocates are released before returning, and so there is no
+ /// deinitialization method.
+ /// TODO Remove the Allocator requirement from this function.
+ pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo {
+ const cpu = switch (cross_target.cpu_model) {
+ .native => detectNativeCpuAndFeatures(cross_target),
+ .baseline => baselineCpuAndFeatures(cross_target),
+ .determined_by_cpu_arch => if (cross_target.cpu_arch == null)
+ detectNativeCpuAndFeatures(cross_target)
+ else
+ baselineCpuAndFeatures(cross_target),
+ .explicit => |model| blk: {
+ var adjusted_model = model.toCpu(cross_target.getCpuArch());
+ cross_target.updateCpuFeatures(&adjusted_model.features);
+ break :blk adjusted_model;
+ },
+ };
+
+ var os = Target.Os.defaultVersionRange(cross_target.getOsTag());
+ if (cross_target.os_tag == null) {
+ switch (Target.current.os.tag) {
+ .linux => {
+ const uts = std.os.uname();
+ const release = mem.toSliceConst(u8, @ptrCast([*:0]const u8, &uts.release));
+ if (std.builtin.Version.parse(release)) |ver| {
+ os.version_range.linux.range.min = ver;
+ os.version_range.linux.range.max = ver;
+ } else |err| switch (err) {
+ error.Overflow => {},
+ error.InvalidCharacter => {},
+ error.InvalidVersion => {},
+ }
+ },
+ .windows => {
+ var version_info: std.os.windows.RTL_OSVERSIONINFOW = undefined;
+ version_info.dwOSVersionInfoSize = @sizeOf(@TypeOf(version_info));
+
+ switch (std.os.windows.ntdll.RtlGetVersion(&version_info)) {
+ .SUCCESS => {},
+ else => unreachable,
+ }
+
+ // Starting from the system infos build a NTDDI-like version
+ // constant whose format is:
+ // B0 B1 B2 B3
+ // `---` `` ``--> Sub-version (Starting from Windows 10 onwards)
+ // \ `--> Service pack (Always zero in the constants defined)
+ // `--> OS version (Major & minor)
+ const os_ver: u16 = //
+ @intCast(u16, version_info.dwMajorVersion & 0xff) << 8 |
+ @intCast(u16, version_info.dwMinorVersion & 0xff);
+ const sp_ver: u8 = 0;
+ const sub_ver: u8 = if (os_ver >= 0x0A00) subver: {
+ // There's no other way to obtain this info beside
+ // checking the build number against a known set of
+ // values
+ const known_build_numbers = [_]u32{
+ 10240, 10586, 14393, 15063, 16299, 17134, 17763,
+ 18362, 18363,
+ };
+ var last_idx: usize = 0;
+ for (known_build_numbers) |build, i| {
+ if (version_info.dwBuildNumber >= build)
+ last_idx = i;
+ }
+ break :subver @truncate(u8, last_idx);
+ } else 0;
+
+ const version: u32 = @as(u32, os_ver) << 16 | @as(u32, sp_ver) << 8 | sub_ver;
+
+ os.version_range.windows.max = @intToEnum(Target.Os.WindowsVersion, version);
+ os.version_range.windows.min = @intToEnum(Target.Os.WindowsVersion, version);
+ },
+ .macosx => {
+ var product_version: [32]u8 = undefined;
+ var size: usize = product_version.len;
+
+ // The osproductversion sysctl was introduced first with
+ // High Sierra, thankfully that's also the baseline that Zig
+ // supports
+ std.os.sysctlbynameC(
+ "kern.osproductversion",
+ &product_version,
+ &size,
+ null,
+ 0,
+ ) catch |err| switch (err) {
+ error.UnknownName => unreachable,
+ else => unreachable,
+ };
+
+ const string_version = product_version[0 .. size - 1 :0];
+ if (std.builtin.Version.parse(string_version)) |ver| {
+ os.version_range.semver.min = ver;
+ os.version_range.semver.max = ver;
+ } else |err| switch (err) {
+ error.Overflow => {},
+ error.InvalidCharacter => {},
+ error.InvalidVersion => {},
+ }
+ },
+ .freebsd => {
+ // TODO Detect native operating system version.
+ },
+ else => {},
+ }
+ }
+
+ if (cross_target.os_version_min) |min| switch (min) {
+ .none => {},
+ .semver => |semver| switch (cross_target.getOsTag()) {
+ .linux => os.version_range.linux.range.min = semver,
+ else => os.version_range.semver.min = semver,
+ },
+ .windows => |win_ver| os.version_range.windows.min = win_ver,
+ };
+
+ if (cross_target.os_version_max) |max| switch (max) {
+ .none => {},
+ .semver => |semver| switch (cross_target.getOsTag()) {
+ .linux => os.version_range.linux.range.max = semver,
+ else => os.version_range.semver.max = semver,
+ },
+ .windows => |win_ver| os.version_range.windows.max = win_ver,
+ };
+
+ if (cross_target.glibc_version) |glibc| {
+ assert(cross_target.isGnuLibC());
+ os.version_range.linux.glibc = glibc;
+ }
+
+ return detectAbiAndDynamicLinker(allocator, cpu, os, cross_target);
+ }
+
+ /// First we attempt to use the executable's own binary. If it is dynamically
+ /// linked, then it should answer both the C ABI question and the dynamic linker question.
+ /// If it is statically linked, then we try /usr/bin/env. If that does not provide the answer, then
+ /// we fall back to the defaults.
+ /// TODO Remove the Allocator requirement from this function.
+ fn detectAbiAndDynamicLinker(
+ allocator: *Allocator,
+ cpu: Target.Cpu,
+ os: Target.Os,
+ cross_target: CrossTarget,
+ ) DetectError!NativeTargetInfo {
+ const native_target_has_ld = comptime Target.current.hasDynamicLinker();
+ const is_linux = Target.current.os.tag == .linux;
+ const have_all_info = cross_target.dynamic_linker.get() != null and
+ cross_target.abi != null and (!is_linux or cross_target.abi.?.isGnu());
+ const os_is_non_native = cross_target.os_tag != null;
+ if (!native_target_has_ld or have_all_info or os_is_non_native) {
+ return defaultAbiAndDynamicLinker(cpu, os, cross_target);
+ }
+ // The current target's ABI cannot be relied on for this. For example, we may build the zig
+ // compiler for target riscv64-linux-musl and provide a tarball for users to download.
+ // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined
+ // and supported by Zig. But that means that we must detect the system ABI here rather than
+ // relying on `Target.current`.
+ const all_abis = comptime blk: {
+ assert(@enumToInt(Target.Abi.none) == 0);
+ const fields = std.meta.fields(Target.Abi)[1..];
+ var array: [fields.len]Target.Abi = undefined;
+ inline for (fields) |field, i| {
+ array[i] = @field(Target.Abi, field.name);
+ }
+ break :blk array;
+ };
+ var ld_info_list_buffer: [all_abis.len]LdInfo = undefined;
+ var ld_info_list_len: usize = 0;
+
+ for (all_abis) |abi| {
+ // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and
+ // skip adding it to `ld_info_list`.
+ const target: Target = .{
+ .cpu = cpu,
+ .os = os,
+ .abi = abi,
+ };
+ const ld = target.standardDynamicLinkerPath();
+ if (ld.get() == null) continue;
+
+ ld_info_list_buffer[ld_info_list_len] = .{
+ .ld = ld,
+ .abi = abi,
+ };
+ ld_info_list_len += 1;
+ }
+ const ld_info_list = ld_info_list_buffer[0..ld_info_list_len];
+
+ if (cross_target.dynamic_linker.get()) |explicit_ld| {
+ const explicit_ld_basename = fs.path.basename(explicit_ld);
+ for (ld_info_list) |ld_info| {
+ const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);
+ }
+ }
+
+ // Best case scenario: the executable is dynamically linked, and we can iterate
+ // over our own shared objects and find a dynamic linker.
+ self_exe: {
+ const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator);
+ defer allocator.free(lib_paths);
+
+ var found_ld_info: LdInfo = undefined;
+ var found_ld_path: [:0]const u8 = undefined;
+
+ // Look for dynamic linker.
+ // This is O(N^M) but typical case here is N=2 and M=10.
+ find_ld: for (lib_paths) |lib_path| {
+ for (ld_info_list) |ld_info| {
+ const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);
+ if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) {
+ found_ld_info = ld_info;
+ found_ld_path = lib_path;
+ break :find_ld;
+ }
+ }
+ } else break :self_exe;
+
+ // Look for glibc version.
+ var os_adjusted = os;
+ if (Target.current.os.tag == .linux and found_ld_info.abi.isGnu() and
+ cross_target.glibc_version == null)
+ {
+ for (lib_paths) |lib_path| {
+ if (std.mem.endsWith(u8, lib_path, glibc_so_basename)) {
+ os_adjusted.version_range.linux.glibc = glibcVerFromSO(lib_path) catch |err| switch (err) {
+ error.UnrecognizedGnuLibCFileName => continue,
+ error.InvalidGnuLibCVersion => continue,
+ error.GnuLibCVersionUnavailable => continue,
+ else => |e| return e,
+ };
+ break;
+ }
+ }
+ }
+
+ var result: NativeTargetInfo = .{
+ .target = .{
+ .cpu = cpu,
+ .os = os_adjusted,
+ .abi = cross_target.abi orelse found_ld_info.abi,
+ },
+ .dynamic_linker = if (cross_target.dynamic_linker.get() == null)
+ DynamicLinker.init(found_ld_path)
+ else
+ cross_target.dynamic_linker,
+ };
+ return result;
+ }
+
+ const env_file = std.fs.openFileAbsoluteC("/usr/bin/env", .{}) catch |err| switch (err) {
+ error.NoSpaceLeft => unreachable,
+ error.NameTooLong => unreachable,
+ error.PathAlreadyExists => unreachable,
+ error.SharingViolation => unreachable,
+ error.InvalidUtf8 => unreachable,
+ error.BadPathName => unreachable,
+ error.PipeBusy => unreachable,
+
+ error.IsDir,
+ error.NotDir,
+ error.AccessDenied,
+ error.NoDevice,
+ error.FileNotFound,
+ error.FileTooBig,
+ error.Unexpected,
+ => return defaultAbiAndDynamicLinker(cpu, os, cross_target),
+
+ else => |e| return e,
+ };
+ defer env_file.close();
+
+ // If Zig is statically linked, such as via distributed binary static builds, the above
+ // trick won't work. The next thing we fall back to is the same thing, but for /usr/bin/env.
+ // Since that path is hard-coded into the shebang line of many portable scripts, it's a
+ // reasonably reliable path to check for.
+ return abiAndDynamicLinkerFromFile(env_file, cpu, os, ld_info_list, cross_target) catch |err| switch (err) {
+ error.FileSystem,
+ error.SystemResources,
+ error.SymLinkLoop,
+ error.ProcessFdQuotaExceeded,
+ error.SystemFdQuotaExceeded,
+ => |e| return e,
+
+ error.UnableToReadElfFile,
+ error.InvalidElfClass,
+ error.InvalidElfVersion,
+ error.InvalidElfEndian,
+ error.InvalidElfFile,
+ error.InvalidElfMagic,
+ error.Unexpected,
+ error.UnexpectedEndOfFile,
+ error.NameTooLong,
+ // Finally, we fall back on the standard path.
+ => defaultAbiAndDynamicLinker(cpu, os, cross_target),
+ };
+ }
+
+ const glibc_so_basename = "libc.so.6";
+
+ fn glibcVerFromSO(so_path: [:0]const u8) !std.builtin.Version {
+ var link_buf: [std.os.PATH_MAX]u8 = undefined;
+ const link_name = std.os.readlinkC(so_path.ptr, &link_buf) catch |err| switch (err) {
+ error.AccessDenied => return error.GnuLibCVersionUnavailable,
+ error.FileSystem => return error.FileSystem,
+ error.SymLinkLoop => return error.SymLinkLoop,
+ error.NameTooLong => unreachable,
+ error.FileNotFound => return error.GnuLibCVersionUnavailable,
+ error.SystemResources => return error.SystemResources,
+ error.NotDir => return error.GnuLibCVersionUnavailable,
+ error.Unexpected => return error.GnuLibCVersionUnavailable,
+ };
+ return glibcVerFromLinkName(link_name);
+ }
+
+ fn glibcVerFromLinkName(link_name: []const u8) !std.builtin.Version {
+ // example: "libc-2.3.4.so"
+ // example: "libc-2.27.so"
+ const prefix = "libc-";
+ const suffix = ".so";
+ if (!mem.startsWith(u8, link_name, prefix) or !mem.endsWith(u8, link_name, suffix)) {
+ return error.UnrecognizedGnuLibCFileName;
+ }
+ // chop off "libc-" and ".so"
+ const link_name_chopped = link_name[prefix.len .. link_name.len - suffix.len];
+ return std.builtin.Version.parse(link_name_chopped) catch |err| switch (err) {
+ error.Overflow => return error.InvalidGnuLibCVersion,
+ error.InvalidCharacter => return error.InvalidGnuLibCVersion,
+ error.InvalidVersion => return error.InvalidGnuLibCVersion,
+ };
+ }
+
+ pub const AbiAndDynamicLinkerFromFileError = error{
+ FileSystem,
+ SystemResources,
+ SymLinkLoop,
+ ProcessFdQuotaExceeded,
+ SystemFdQuotaExceeded,
+ UnableToReadElfFile,
+ InvalidElfClass,
+ InvalidElfVersion,
+ InvalidElfEndian,
+ InvalidElfFile,
+ InvalidElfMagic,
+ Unexpected,
+ UnexpectedEndOfFile,
+ NameTooLong,
+ };
+
+ pub fn abiAndDynamicLinkerFromFile(
+ file: fs.File,
+ cpu: Target.Cpu,
+ os: Target.Os,
+ ld_info_list: []const LdInfo,
+ cross_target: CrossTarget,
+ ) AbiAndDynamicLinkerFromFileError!NativeTargetInfo {
+ var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
+ _ = try preadFull(file, &hdr_buf, 0, hdr_buf.len);
+ const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf);
+ const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf);
+ if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
+ const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) {
+ elf.ELFDATA2LSB => .Little,
+ elf.ELFDATA2MSB => .Big,
+ else => return error.InvalidElfEndian,
+ };
+ const need_bswap = elf_endian != std.builtin.endian;
+ if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
+
+ const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) {
+ elf.ELFCLASS32 => false,
+ elf.ELFCLASS64 => true,
+ else => return error.InvalidElfClass,
+ };
+ var phoff = elfInt(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff);
+ const phentsize = elfInt(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize);
+ const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum);
+
+ var result: NativeTargetInfo = .{
+ .target = .{
+ .cpu = cpu,
+ .os = os,
+ .abi = cross_target.abi orelse Target.Abi.default(cpu.arch, os),
+ },
+ .dynamic_linker = cross_target.dynamic_linker,
+ };
+ var rpath_offset: ?u64 = null; // Found inside PT_DYNAMIC
+ const look_for_ld = cross_target.dynamic_linker.get() == null;
+
+ var ph_buf: [16 * @sizeOf(elf.Elf64_Phdr)]u8 align(@alignOf(elf.Elf64_Phdr)) = undefined;
+ if (phentsize > @sizeOf(elf.Elf64_Phdr)) return error.InvalidElfFile;
+
+ var ph_i: u16 = 0;
+ while (ph_i < phnum) {
+ // Reserve some bytes so that we can deref the 64-bit struct fields
+ // even when the ELF file is 32-bits.
+ const ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr);
+ const ph_read_byte_len = try preadFull(file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize);
+ var ph_buf_i: usize = 0;
+ while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({
+ ph_i += 1;
+ phoff += phentsize;
+ ph_buf_i += phentsize;
+ }) {
+ const ph32 = @ptrCast(*elf.Elf32_Phdr, @alignCast(@alignOf(elf.Elf32_Phdr), &ph_buf[ph_buf_i]));
+ const ph64 = @ptrCast(*elf.Elf64_Phdr, @alignCast(@alignOf(elf.Elf64_Phdr), &ph_buf[ph_buf_i]));
+ const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type);
+ switch (p_type) {
+ elf.PT_INTERP => if (look_for_ld) {
+ const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
+ const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
+ if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
+ _ = try preadFull(file, result.dynamic_linker.buffer[0..p_filesz], p_offset, p_filesz);
+ // PT_INTERP includes a null byte in p_filesz.
+ const len = p_filesz - 1;
+ // dynamic_linker.max_byte is "max", not "len".
+ // We know it will fit in u8 because we check against dynamic_linker.buffer.len above.
+ result.dynamic_linker.max_byte = @intCast(u8, len - 1);
+
+ // Use it to determine ABI.
+ const full_ld_path = result.dynamic_linker.buffer[0..len];
+ for (ld_info_list) |ld_info| {
+ const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);
+ if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) {
+ result.target.abi = ld_info.abi;
+ break;
+ }
+ }
+ },
+ // We only need this for detecting glibc version.
+ elf.PT_DYNAMIC => if (Target.current.os.tag == .linux and result.target.isGnuLibC() and
+ cross_target.glibc_version == null)
+ {
+ var dyn_off = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
+ const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
+ const dyn_size: u64 = if (is_64) @sizeOf(elf.Elf64_Dyn) else @sizeOf(elf.Elf32_Dyn);
+ const dyn_num = p_filesz / dyn_size;
+ var dyn_buf: [16 * @sizeOf(elf.Elf64_Dyn)]u8 align(@alignOf(elf.Elf64_Dyn)) = undefined;
+ var dyn_i: usize = 0;
+ dyn: while (dyn_i < dyn_num) {
+ // Reserve some bytes so that we can deref the 64-bit struct fields
+ // even when the ELF file is 32-bits.
+ const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn);
+ const dyn_read_byte_len = try preadFull(
+ file,
+ dyn_buf[0 .. dyn_buf.len - dyn_reserve],
+ dyn_off,
+ dyn_size,
+ );
+ var dyn_buf_i: usize = 0;
+ while (dyn_buf_i < dyn_read_byte_len and dyn_i < dyn_num) : ({
+ dyn_i += 1;
+ dyn_off += dyn_size;
+ dyn_buf_i += dyn_size;
+ }) {
+ const dyn32 = @ptrCast(
+ *elf.Elf32_Dyn,
+ @alignCast(@alignOf(elf.Elf32_Dyn), &dyn_buf[dyn_buf_i]),
+ );
+ const dyn64 = @ptrCast(
+ *elf.Elf64_Dyn,
+ @alignCast(@alignOf(elf.Elf64_Dyn), &dyn_buf[dyn_buf_i]),
+ );
+ const tag = elfInt(is_64, need_bswap, dyn32.d_tag, dyn64.d_tag);
+ const val = elfInt(is_64, need_bswap, dyn32.d_val, dyn64.d_val);
+ if (tag == elf.DT_RUNPATH) {
+ rpath_offset = val;
+ break :dyn;
+ }
+ }
+ }
+ },
+ else => continue,
+ }
+ }
+ }
+
+ if (Target.current.os.tag == .linux and result.target.isGnuLibC() and cross_target.glibc_version == null) {
+ if (rpath_offset) |rpoff| {
+ const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
+
+ var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff);
+ const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize);
+ const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx);
+
+ var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
+ if (sh_buf.len < shentsize) return error.InvalidElfFile;
+
+ _ = try preadFull(file, &sh_buf, str_section_off, shentsize);
+ const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf));
+ const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf));
+ const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
+ const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
+ var strtab_buf: [4096:0]u8 = undefined;
+ const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len);
+ const shstrtab_read_len = try preadFull(file, &strtab_buf, shstrtab_off, shstrtab_len);
+ const shstrtab = strtab_buf[0..shstrtab_read_len];
+
+ const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
+ var sh_i: u16 = 0;
+ const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) {
+ // Reserve some bytes so that we can deref the 64-bit struct fields
+ // even when the ELF file is 32-bits.
+ const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
+ const sh_read_byte_len = try preadFull(
+ file,
+ sh_buf[0 .. sh_buf.len - sh_reserve],
+ shoff,
+ shentsize,
+ );
+ var sh_buf_i: usize = 0;
+ while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({
+ sh_i += 1;
+ shoff += shentsize;
+ sh_buf_i += shentsize;
+ }) {
+ const sh32 = @ptrCast(
+ *elf.Elf32_Shdr,
+ @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf[sh_buf_i]),
+ );
+ const sh64 = @ptrCast(
+ *elf.Elf64_Shdr,
+ @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf[sh_buf_i]),
+ );
+ const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);
+ // TODO this pointer cast should not be necessary
+ const sh_name = mem.toSliceConst(u8, @ptrCast([*:0]u8, shstrtab[sh_name_off..].ptr));
+ if (mem.eql(u8, sh_name, ".dynstr")) {
+ break :find_dyn_str .{
+ .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),
+ .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size),
+ };
+ }
+ }
+ } else null;
+
+ if (dynstr) |ds| {
+ const strtab_len = std.math.min(ds.size, strtab_buf.len);
+ const strtab_read_len = try preadFull(file, &strtab_buf, ds.offset, shstrtab_len);
+ const strtab = strtab_buf[0..strtab_read_len];
+ // TODO this pointer cast should not be necessary
+ const rpath_list = mem.toSliceConst(u8, @ptrCast([*:0]u8, strtab[rpoff..].ptr));
+ var it = mem.tokenize(rpath_list, ":");
+ while (it.next()) |rpath| {
+ var dir = fs.cwd().openDirList(rpath) catch |err| switch (err) {
+ error.NameTooLong => unreachable,
+ error.InvalidUtf8 => unreachable,
+ error.BadPathName => unreachable,
+ error.DeviceBusy => unreachable,
+
+ error.FileNotFound,
+ error.NotDir,
+ error.AccessDenied,
+ error.NoDevice,
+ => continue,
+
+ error.ProcessFdQuotaExceeded,
+ error.SystemFdQuotaExceeded,
+ error.SystemResources,
+ error.SymLinkLoop,
+ error.Unexpected,
+ => |e| return e,
+ };
+ defer dir.close();
+
+ var link_buf: [std.os.PATH_MAX]u8 = undefined;
+ const link_name = std.os.readlinkatC(
+ dir.fd,
+ glibc_so_basename,
+ &link_buf,
+ ) catch |err| switch (err) {
+ error.NameTooLong => unreachable,
+
+ error.AccessDenied,
+ error.FileNotFound,
+ error.NotDir,
+ => continue,
+
+ error.SystemResources,
+ error.FileSystem,
+ error.SymLinkLoop,
+ error.Unexpected,
+ => |e| return e,
+ };
+ result.target.os.version_range.linux.glibc = glibcVerFromLinkName(
+ link_name,
+ ) catch |err| switch (err) {
+ error.UnrecognizedGnuLibCFileName,
+ error.InvalidGnuLibCVersion,
+ => continue,
+ };
+ break;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ fn preadFull(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize {
+ var i: u64 = 0;
+ while (i < min_read_len) {
+ const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) {
+ error.OperationAborted => unreachable, // Windows-only
+ error.WouldBlock => unreachable, // Did not request blocking mode
+ error.SystemResources => return error.SystemResources,
+ error.IsDir => return error.UnableToReadElfFile,
+ error.BrokenPipe => return error.UnableToReadElfFile,
+ error.ConnectionResetByPeer => return error.UnableToReadElfFile,
+ error.Unexpected => return error.Unexpected,
+ error.InputOutput => return error.FileSystem,
+ };
+ if (len == 0) return error.UnexpectedEndOfFile;
+ i += len;
+ }
+ return i;
+ }
+
+ fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, cross_target: CrossTarget) !NativeTargetInfo {
+ const target: Target = .{
+ .cpu = cpu,
+ .os = os,
+ .abi = cross_target.abi orelse Target.Abi.default(cpu.arch, os),
+ };
+ return NativeTargetInfo{
+ .target = target,
+ .dynamic_linker = if (cross_target.dynamic_linker.get() == null)
+ target.standardDynamicLinkerPath()
+ else
+ cross_target.dynamic_linker,
+ };
+ }
+
+ pub const LdInfo = struct {
+ ld: DynamicLinker,
+ abi: Target.Abi,
+ };
+
+ fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {
+ if (is_64) {
+ if (need_bswap) {
+ return @byteSwap(@TypeOf(int_64), int_64);
+ } else {
+ return int_64;
+ }
+ } else {
+ if (need_bswap) {
+ return @byteSwap(@TypeOf(int_32), int_32);
+ } else {
+ return int_32;
+ }
+ }
+ }
+
+ fn detectNativeCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {
+ // TODO Detect native CPU model & features. Until that is implemented we use baseline.
+ return baselineCpuAndFeatures(cross_target);
+ }
+
+ fn baselineCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {
+ var adjusted_baseline = Target.Cpu.baseline(cross_target.getCpuArch());
+ cross_target.updateCpuFeatures(&adjusted_baseline.features);
+ return adjusted_baseline;
+ }
+};
diff --git a/src-self-hosted/c_int.zig b/src-self-hosted/c_int.zig
index 2a840372b9..1ee27c7596 100644
--- a/src-self-hosted/c_int.zig
+++ b/src-self-hosted/c_int.zig
@@ -70,7 +70,7 @@ pub const CInt = struct {
pub fn sizeInBits(cint: CInt, self: Target) u32 {
const arch = self.getArch();
- switch (self.getOs()) {
+ switch (self.os.tag) {
.freestanding, .other => switch (self.getArch()) {
.msp430 => switch (cint.id) {
.Short,
diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig
index 08a10f8378..8a30fb99f3 100644
--- a/src-self-hosted/clang.zig
+++ b/src-self-hosted/clang.zig
@@ -1072,7 +1072,7 @@ pub const struct_ZigClangExprEvalResult = extern struct {
pub const struct_ZigClangAPValue = extern struct {
Kind: ZigClangAPValueKind,
- Data: if (builtin.os == .windows and builtin.abi == .msvc) [52]u8 else [68]u8,
+ Data: if (builtin.os.tag == .windows and builtin.abi == .msvc) [52]u8 else [68]u8,
};
pub extern fn ZigClangVarDecl_getTypeSourceInfo_getType(self: *const struct_ZigClangVarDecl) struct_ZigClangQualType;
diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig
index 11838e7e63..c7f5690cc3 100644
--- a/src-self-hosted/introspect.zig
+++ b/src-self-hosted/introspect.zig
@@ -1,4 +1,4 @@
-// Introspection and determination of system libraries needed by zig.
+//! Introspection and determination of system libraries needed by zig.
const std = @import("std");
const mem = std.mem;
@@ -6,14 +6,6 @@ const fs = std.fs;
const warn = std.debug.warn;
-pub fn detectDynamicLinker(allocator: *mem.Allocator, target: std.Target) ![:0]u8 {
- if (target == .Native) {
- return @import("libc_installation.zig").detectNativeDynamicLinker(allocator);
- } else {
- return target.getStandardDynamicLinkerPath(allocator);
- }
-}
-
/// Caller must free result
pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 {
const test_zig_dir = try fs.path.join(allocator, &[_][]const u8{ test_path, "lib", "zig" });
diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig
index 576294a7d7..2e65962d41 100644
--- a/src-self-hosted/ir.zig
+++ b/src-self-hosted/ir.zig
@@ -1803,7 +1803,7 @@ pub const Builder = struct {
// Look at the params and ref() other instructions
inline for (@typeInfo(I.Params).Struct.fields) |f| {
- switch (f.fiedl_type) {
+ switch (f.field_type) {
*Inst => @field(inst.params, f.name).ref(self),
*BasicBlock => @field(inst.params, f.name).ref(self),
?*Inst => if (@field(inst.params, f.name)) |other| other.ref(self),
diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig
index 85740240ab..41def38126 100644
--- a/src-self-hosted/libc_installation.zig
+++ b/src-self-hosted/libc_installation.zig
@@ -7,11 +7,7 @@ const Allocator = std.mem.Allocator;
const Batch = std.event.Batch;
const is_darwin = Target.current.isDarwin();
-const is_windows = Target.current.isWindows();
-const is_freebsd = Target.current.isFreeBSD();
-const is_netbsd = Target.current.isNetBSD();
-const is_linux = Target.current.isLinux();
-const is_dragonfly = Target.current.isDragonFlyBSD();
+const is_windows = Target.current.os.tag == .windows;
const is_gnu = Target.current.isGnu();
usingnamespace @import("windows_sdk.zig");
@@ -99,27 +95,27 @@ pub const LibCInstallation = struct {
return error.ParseError;
}
if (self.crt_dir == null and !is_darwin) {
- try stderr.print("crt_dir may not be empty for {}\n", .{@tagName(Target.current.getOs())});
+ try stderr.print("crt_dir may not be empty for {}\n", .{@tagName(Target.current.os.tag)});
return error.ParseError;
}
if (self.static_crt_dir == null and is_windows and is_gnu) {
try stderr.print("static_crt_dir may not be empty for {}-{}\n", .{
- @tagName(Target.current.getOs()),
- @tagName(Target.current.getAbi()),
+ @tagName(Target.current.os.tag),
+ @tagName(Target.current.abi),
});
return error.ParseError;
}
if (self.msvc_lib_dir == null and is_windows and !is_gnu) {
try stderr.print("msvc_lib_dir may not be empty for {}-{}\n", .{
- @tagName(Target.current.getOs()),
- @tagName(Target.current.getAbi()),
+ @tagName(Target.current.os.tag),
+ @tagName(Target.current.abi),
});
return error.ParseError;
}
if (self.kernel32_lib_dir == null and is_windows and !is_gnu) {
try stderr.print("kernel32_lib_dir may not be empty for {}-{}\n", .{
- @tagName(Target.current.getOs()),
- @tagName(Target.current.getAbi()),
+ @tagName(Target.current.os.tag),
+ @tagName(Target.current.abi),
});
return error.ParseError;
}
@@ -216,10 +212,10 @@ pub const LibCInstallation = struct {
var batch = Batch(FindError!void, 2, .auto_async).init();
errdefer batch.wait() catch {};
batch.add(&async self.findNativeIncludeDirPosix(args));
- if (is_freebsd or is_netbsd) {
- self.crt_dir = try std.mem.dupeZ(args.allocator, u8, "/usr/lib");
- } else if (is_linux or is_dragonfly) {
- batch.add(&async self.findNativeCrtDirPosix(args));
+ switch (Target.current.os.tag) {
+ .freebsd, .netbsd => self.crt_dir = try std.mem.dupeZ(args.allocator, u8, "/usr/lib"),
+ .linux, .dragonfly => batch.add(&async self.findNativeCrtDirPosix(args)),
+ else => {},
}
break :blk batch.wait();
};
@@ -616,104 +612,6 @@ fn printVerboseInvocation(
}
}
-/// Caller owns returned memory.
-pub fn detectNativeDynamicLinker(allocator: *Allocator) error{
- OutOfMemory,
- TargetHasNoDynamicLinker,
- UnknownDynamicLinkerPath,
-}![:0]u8 {
- if (!comptime Target.current.hasDynamicLinker()) {
- return error.TargetHasNoDynamicLinker;
- }
-
- // The current target's ABI cannot be relied on for this. For example, we may build the zig
- // compiler for target riscv64-linux-musl and provide a tarball for users to download.
- // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined
- // and supported by Zig. But that means that we must detect the system ABI here rather than
- // relying on `std.Target.current`.
-
- const LdInfo = struct {
- ld_path: []u8,
- abi: Target.Abi,
- };
- var ld_info_list = std.ArrayList(LdInfo).init(allocator);
- defer {
- for (ld_info_list.toSlice()) |ld_info| allocator.free(ld_info.ld_path);
- ld_info_list.deinit();
- }
-
- const all_abis = comptime blk: {
- const fields = std.meta.fields(Target.Abi);
- var array: [fields.len]Target.Abi = undefined;
- inline for (fields) |field, i| {
- array[i] = @field(Target.Abi, field.name);
- }
- break :blk array;
- };
- for (all_abis) |abi| {
- // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and
- // skip adding it to `ld_info_list`.
- const target: Target = .{
- .Cross = .{
- .cpu = Target.Cpu.baseline(Target.current.getArch()),
- .os = Target.current.getOs(),
- .abi = abi,
- },
- };
- const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue,
- };
- errdefer allocator.free(standard_ld_path);
- try ld_info_list.append(.{
- .ld_path = standard_ld_path,
- .abi = abi,
- });
- }
-
- // Best case scenario: the zig compiler is dynamically linked, and we can iterate
- // over our own shared objects and find a dynamic linker.
- {
- const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator);
- defer allocator.free(lib_paths);
-
- // This is O(N^M) but typical case here is N=2 and M=10.
- for (lib_paths) |lib_path| {
- for (ld_info_list.toSlice()) |ld_info| {
- const standard_ld_basename = fs.path.basename(ld_info.ld_path);
- if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) {
- return std.mem.dupeZ(allocator, u8, lib_path);
- }
- }
- }
- }
-
- // If Zig is statically linked, such as via distributed binary static builds, the above
- // trick won't work. What are we left with? Try to run the system C compiler and get
- // it to tell us the dynamic linker path.
- // TODO: instead of this, look at the shared libs of /usr/bin/env.
- for (ld_info_list.toSlice()) |ld_info| {
- const standard_ld_basename = fs.path.basename(ld_info.ld_path);
-
- const full_ld_path = ccPrintFileName(.{
- .allocator = allocator,
- .search_basename = standard_ld_basename,
- .want_dirname = .full_path,
- }) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.LibCRuntimeNotFound,
- error.CCompilerExitCode,
- error.CCompilerCrashed,
- error.UnableToSpawnCCompiler,
- => continue,
- };
- return full_ld_path;
- }
-
- // Finally, we fall back on the standard path.
- return Target.current.getStandardDynamicLinkerPath(allocator);
-}
-
const Search = struct {
path: []const u8,
version: []const u8,
diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig
index 0a29da4778..1efa15574a 100644
--- a/src-self-hosted/link.zig
+++ b/src-self-hosted/link.zig
@@ -515,7 +515,7 @@ const DarwinPlatform = struct {
break :blk ver;
},
.None => blk: {
- assert(comp.target.getOs() == .macosx);
+ assert(comp.target.os.tag == .macosx);
result.kind = .MacOS;
break :blk "10.14";
},
@@ -534,7 +534,7 @@ const DarwinPlatform = struct {
}
if (result.kind == .IPhoneOS) {
- switch (comp.target.getArch()) {
+ switch (comp.target.cpu.arch) {
.i386,
.x86_64,
=> result.kind = .IPhoneOSSimulator,
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index fbfd1e2642..4446a974d4 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -79,9 +79,9 @@ pub fn main() !void {
} else if (mem.eql(u8, cmd, "libc")) {
return cmdLibC(allocator, cmd_args);
} else if (mem.eql(u8, cmd, "targets")) {
- // TODO figure out the current target rather than using the target that was specified when
- // compiling the compiler
- return @import("print_targets.zig").cmdTargets(allocator, cmd_args, stdout, Target.current);
+ const info = try std.zig.system.NativeTargetInfo.detect(allocator);
+ defer info.deinit(allocator);
+ return @import("print_targets.zig").cmdTargets(allocator, cmd_args, stdout, info.target);
} else if (mem.eql(u8, cmd, "version")) {
return cmdVersion(allocator, cmd_args);
} else if (mem.eql(u8, cmd, "zen")) {
@@ -792,7 +792,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
}
fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void {
- try stdout.print("{}\n", .{std.mem.toSliceConst(u8, c.ZIG_VERSION_STRING)});
+ try stdout.print("{}\n", .{c.ZIG_VERSION_STRING});
}
fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void {
@@ -863,12 +863,12 @@ fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void {
\\ZIG_DIA_GUIDS_LIB {}
\\
, .{
- std.mem.toSliceConst(u8, c.ZIG_CMAKE_BINARY_DIR),
- std.mem.toSliceConst(u8, c.ZIG_CXX_COMPILER),
- std.mem.toSliceConst(u8, c.ZIG_LLD_INCLUDE_PATH),
- std.mem.toSliceConst(u8, c.ZIG_LLD_LIBRARIES),
- std.mem.toSliceConst(u8, c.ZIG_LLVM_CONFIG_EXE),
- std.mem.toSliceConst(u8, c.ZIG_DIA_GUIDS_LIB),
+ c.ZIG_CMAKE_BINARY_DIR,
+ c.ZIG_CXX_COMPILER,
+ c.ZIG_LLD_INCLUDE_PATH,
+ c.ZIG_LLD_LIBRARIES,
+ c.ZIG_LLVM_CONFIG_EXE,
+ c.ZIG_DIA_GUIDS_LIB,
});
}
diff --git a/src-self-hosted/print_targets.zig b/src-self-hosted/print_targets.zig
index 16f1891164..be024a2a04 100644
--- a/src-self-hosted/print_targets.zig
+++ b/src-self-hosted/print_targets.zig
@@ -124,7 +124,7 @@ pub fn cmdTargets(
try jws.objectField("os");
try jws.beginArray();
- inline for (@typeInfo(Target.Os).Enum.fields) |field| {
+ inline for (@typeInfo(Target.Os.Tag).Enum.fields) |field| {
try jws.arrayElem();
try jws.emitString(field.name);
}
@@ -201,16 +201,16 @@ pub fn cmdTargets(
try jws.objectField("cpu");
try jws.beginObject();
try jws.objectField("arch");
- try jws.emitString(@tagName(native_target.getArch()));
+ try jws.emitString(@tagName(native_target.cpu.arch));
try jws.objectField("name");
- const cpu = native_target.getCpu();
+ const cpu = native_target.cpu;
try jws.emitString(cpu.model.name);
{
try jws.objectField("features");
try jws.beginArray();
- for (native_target.getArch().allFeaturesList()) |feature, i_usize| {
+ for (native_target.cpu.arch.allFeaturesList()) |feature, i_usize| {
const index = @intCast(Target.Cpu.Feature.Set.Index, i_usize);
if (cpu.features.isEnabled(index)) {
try jws.arrayElem();
@@ -222,9 +222,9 @@ pub fn cmdTargets(
try jws.endObject();
}
try jws.objectField("os");
- try jws.emitString(@tagName(native_target.getOs()));
+ try jws.emitString(@tagName(native_target.os.tag));
try jws.objectField("abi");
- try jws.emitString(@tagName(native_target.getAbi()));
+ try jws.emitString(@tagName(native_target.abi));
// TODO implement native glibc version detection in self-hosted
try jws.endObject();
diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig
index dacdb86bcd..a8aa10d91e 100644
--- a/src-self-hosted/stage2.zig
+++ b/src-self-hosted/stage2.zig
@@ -10,6 +10,7 @@ const Allocator = mem.Allocator;
const ArrayList = std.ArrayList;
const Buffer = std.Buffer;
const Target = std.Target;
+const CrossTarget = std.zig.CrossTarget;
const self_hosted_main = @import("main.zig");
const errmsg = @import("errmsg.zig");
const DepTokenizer = @import("dep_tokenizer.zig").Tokenizer;
@@ -87,7 +88,7 @@ const Error = extern enum {
NotLazy,
IsAsync,
ImportOutsidePkgPath,
- UnknownCpu,
+ UnknownCpuModel,
UnknownCpuFeature,
InvalidCpuFeatures,
InvalidLlvmCpuFeaturesFormat,
@@ -110,6 +111,8 @@ const Error = extern enum {
WindowsSdkNotFound,
UnknownDynamicLinkerPath,
TargetHasNoDynamicLinker,
+ InvalidAbiVersion,
+ InvalidOperatingSystemVersion,
};
const FILE = std.c.FILE;
@@ -632,13 +635,9 @@ export fn stage2_cmd_targets(zig_triple: [*:0]const u8) c_int {
}
fn cmdTargets(zig_triple: [*:0]const u8) !void {
- var target = try Target.parse(.{ .arch_os_abi = mem.toSliceConst(u8, zig_triple) });
- target.Cross.cpu = blk: {
- const llvm = @import("llvm.zig");
- const llvm_cpu_name = llvm.GetHostCPUName();
- const llvm_cpu_features = llvm.GetNativeFeatures();
- break :blk try detectNativeCpuWithLLVM(target.getArch(), llvm_cpu_name, llvm_cpu_features);
- };
+ var cross_target = try CrossTarget.parse(.{ .arch_os_abi = mem.toSliceConst(u8, zig_triple) });
+ var dynamic_linker: ?[*:0]u8 = null;
+ const target = try crossTargetToTarget(cross_target, &dynamic_linker);
return @import("print_targets.zig").cmdTargets(
std.heap.c_allocator,
&[0][]u8{},
@@ -652,16 +651,24 @@ export fn stage2_target_parse(
target: *Stage2Target,
zig_triple: ?[*:0]const u8,
mcpu: ?[*:0]const u8,
+ dynamic_linker: ?[*:0]const u8,
) Error {
- stage2TargetParse(target, zig_triple, mcpu) catch |err| switch (err) {
+ stage2TargetParse(target, zig_triple, mcpu, dynamic_linker) catch |err| switch (err) {
error.OutOfMemory => return .OutOfMemory,
error.UnknownArchitecture => return .UnknownArchitecture,
error.UnknownOperatingSystem => return .UnknownOperatingSystem,
error.UnknownApplicationBinaryInterface => return .UnknownApplicationBinaryInterface,
error.MissingOperatingSystem => return .MissingOperatingSystem,
- error.MissingArchitecture => return .MissingArchitecture,
error.InvalidLlvmCpuFeaturesFormat => return .InvalidLlvmCpuFeaturesFormat,
error.UnexpectedExtraField => return .SemanticAnalyzeFail,
+ error.InvalidAbiVersion => return .InvalidAbiVersion,
+ error.InvalidOperatingSystemVersion => return .InvalidOperatingSystemVersion,
+ error.FileSystem => return .FileSystem,
+ error.SymLinkLoop => return .SymLinkLoop,
+ error.SystemResources => return .SystemResources,
+ error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded,
+ error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded,
+ error.DeviceBusy => return .DeviceBusy,
};
return .None;
}
@@ -670,17 +677,20 @@ fn stage2TargetParse(
stage1_target: *Stage2Target,
zig_triple_oz: ?[*:0]const u8,
mcpu_oz: ?[*:0]const u8,
+ dynamic_linker_oz: ?[*:0]const u8,
) !void {
- const target: Target = if (zig_triple_oz) |zig_triple_z| blk: {
+ const target: CrossTarget = if (zig_triple_oz) |zig_triple_z| blk: {
const zig_triple = mem.toSliceConst(u8, zig_triple_z);
- const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else "baseline";
- var diags: std.Target.ParseOptions.Diagnostics = .{};
- break :blk Target.parse(.{
+ const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else null;
+ const dynamic_linker = if (dynamic_linker_oz) |dl_z| mem.toSliceConst(u8, dl_z) else null;
+ var diags: CrossTarget.ParseOptions.Diagnostics = .{};
+ break :blk CrossTarget.parse(.{
.arch_os_abi = zig_triple,
.cpu_features = mcpu,
+ .dynamic_linker = dynamic_linker,
.diagnostics = &diags,
}) catch |err| switch (err) {
- error.UnknownCpu => {
+ error.UnknownCpuModel => {
std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
diags.cpu_name.?,
@tagName(diags.arch.?),
@@ -706,73 +716,11 @@ fn stage2TargetParse(
},
else => |e| return e,
};
- } else Target.Native;
+ } else .{};
try stage1_target.fromTarget(target);
}
-fn initStage1TargetCpuFeatures(stage1_target: *Stage2Target, cpu: Target.Cpu) !void {
- const allocator = std.heap.c_allocator;
- const cache_hash = try std.fmt.allocPrint0(allocator, "{}\n{}", .{
- cpu.model.name,
- cpu.features.asBytes(),
- });
- errdefer allocator.free(cache_hash);
-
- const generic_arch_name = cpu.arch.genericName();
- var builtin_str_buffer = try std.Buffer.allocPrint(allocator,
- \\Cpu{{
- \\ .arch = .{},
- \\ .model = &Target.{}.cpu.{},
- \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{
- \\
- , .{
- @tagName(cpu.arch),
- generic_arch_name,
- cpu.model.name,
- generic_arch_name,
- generic_arch_name,
- });
- defer builtin_str_buffer.deinit();
-
- var llvm_features_buffer = try std.Buffer.initSize(allocator, 0);
- defer llvm_features_buffer.deinit();
-
- for (cpu.arch.allFeaturesList()) |feature, index_usize| {
- const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize);
- const is_enabled = cpu.features.isEnabled(index);
-
- if (feature.llvm_name) |llvm_name| {
- const plus_or_minus = "-+"[@boolToInt(is_enabled)];
- try llvm_features_buffer.appendByte(plus_or_minus);
- try llvm_features_buffer.append(llvm_name);
- try llvm_features_buffer.append(",");
- }
-
- if (is_enabled) {
- // TODO some kind of "zig identifier escape" function rather than
- // unconditionally using @"" syntax
- try builtin_str_buffer.append(" .@\"");
- try builtin_str_buffer.append(feature.name);
- try builtin_str_buffer.append("\",\n");
- }
- }
-
- try builtin_str_buffer.append(
- \\ }),
- \\};
- \\
- );
-
- assert(mem.endsWith(u8, llvm_features_buffer.toSliceConst(), ","));
- llvm_features_buffer.shrink(llvm_features_buffer.len() - 1);
-
- stage1_target.llvm_cpu_name = if (cpu.model.llvm_name) |s| s.ptr else null;
- stage1_target.llvm_cpu_features = llvm_features_buffer.toOwnedSlice().ptr;
- stage1_target.builtin_str = builtin_str_buffer.toOwnedSlice().ptr;
- stage1_target.cache_hash = cache_hash.ptr;
-}
-
// ABI warning
const Stage2LibCInstallation = extern struct {
include_dir: [*:0]const u8,
@@ -948,15 +896,18 @@ const Stage2Target = extern struct {
is_native: bool,
- glibc_version: ?*Stage2GLibCVersion, // null means default
+ glibc_or_darwin_version: ?*Stage2SemVer,
llvm_cpu_name: ?[*:0]const u8,
llvm_cpu_features: ?[*:0]const u8,
- builtin_str: ?[*:0]const u8,
+ cpu_builtin_str: ?[*:0]const u8,
cache_hash: ?[*:0]const u8,
+ os_builtin_str: ?[*:0]const u8,
- fn toTarget(in_target: Stage2Target) Target {
- if (in_target.is_native) return .Native;
+ dynamic_linker: ?[*:0]const u8,
+
+ fn toTarget(in_target: Stage2Target) CrossTarget {
+ if (in_target.is_native) return .{};
const in_arch = in_target.arch - 1; // skip over ZigLLVM_UnknownArch
const in_os = in_target.os;
@@ -965,66 +916,270 @@ const Stage2Target = extern struct {
return .{
.Cross = .{
.cpu = Target.Cpu.baseline(enumInt(Target.Cpu.Arch, in_arch)),
- .os = enumInt(Target.Os, in_os),
+ .os = Target.Os.defaultVersionRange(enumInt(Target.Os.Tag, in_os)),
.abi = enumInt(Target.Abi, in_abi),
},
};
}
- fn fromTarget(self: *Stage2Target, target: Target) !void {
- const cpu = switch (target) {
- .Native => blk: {
- // TODO self-host CPU model and feature detection instead of relying on LLVM
- const llvm = @import("llvm.zig");
- const llvm_cpu_name = llvm.GetHostCPUName();
- const llvm_cpu_features = llvm.GetNativeFeatures();
- break :blk try detectNativeCpuWithLLVM(target.getArch(), llvm_cpu_name, llvm_cpu_features);
- },
- .Cross => target.getCpu(),
+ fn fromTarget(self: *Stage2Target, cross_target: CrossTarget) !void {
+ const allocator = std.heap.c_allocator;
+
+ var dynamic_linker: ?[*:0]u8 = null;
+ const target = try crossTargetToTarget(cross_target, &dynamic_linker);
+
+ var cache_hash = try std.Buffer.allocPrint(allocator, "{}\n{}\n", .{
+ target.cpu.model.name,
+ target.cpu.features.asBytes(),
+ });
+ defer cache_hash.deinit();
+
+ const generic_arch_name = target.cpu.arch.genericName();
+ var cpu_builtin_str_buffer = try std.Buffer.allocPrint(allocator,
+ \\Cpu{{
+ \\ .arch = .{},
+ \\ .model = &Target.{}.cpu.{},
+ \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{
+ \\
+ , .{
+ @tagName(target.cpu.arch),
+ generic_arch_name,
+ target.cpu.model.name,
+ generic_arch_name,
+ generic_arch_name,
+ });
+ defer cpu_builtin_str_buffer.deinit();
+
+ var llvm_features_buffer = try std.Buffer.initSize(allocator, 0);
+ defer llvm_features_buffer.deinit();
+
+ for (target.cpu.arch.allFeaturesList()) |feature, index_usize| {
+ const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize);
+ const is_enabled = target.cpu.features.isEnabled(index);
+
+ if (feature.llvm_name) |llvm_name| {
+ const plus_or_minus = "-+"[@boolToInt(is_enabled)];
+ try llvm_features_buffer.appendByte(plus_or_minus);
+ try llvm_features_buffer.append(llvm_name);
+ try llvm_features_buffer.append(",");
+ }
+
+ if (is_enabled) {
+ // TODO some kind of "zig identifier escape" function rather than
+ // unconditionally using @"" syntax
+ try cpu_builtin_str_buffer.append(" .@\"");
+ try cpu_builtin_str_buffer.append(feature.name);
+ try cpu_builtin_str_buffer.append("\",\n");
+ }
+ }
+
+ try cpu_builtin_str_buffer.append(
+ \\ }),
+ \\};
+ \\
+ );
+
+ assert(mem.endsWith(u8, llvm_features_buffer.toSliceConst(), ","));
+ llvm_features_buffer.shrink(llvm_features_buffer.len() - 1);
+
+ var os_builtin_str_buffer = try std.Buffer.allocPrint(allocator,
+ \\Os{{
+ \\ .tag = .{},
+ \\ .version_range = .{{
+ , .{@tagName(target.os.tag)});
+ defer os_builtin_str_buffer.deinit();
+
+ // We'll re-use the OS version range builtin string for the cache hash.
+ const os_builtin_str_ver_start_index = os_builtin_str_buffer.len();
+
+ @setEvalBranchQuota(2000);
+ switch (target.os.tag) {
+ .freestanding,
+ .ananas,
+ .cloudabi,
+ .dragonfly,
+ .fuchsia,
+ .ios,
+ .kfreebsd,
+ .lv2,
+ .solaris,
+ .haiku,
+ .minix,
+ .rtems,
+ .nacl,
+ .cnk,
+ .aix,
+ .cuda,
+ .nvcl,
+ .amdhsa,
+ .ps4,
+ .elfiamcu,
+ .tvos,
+ .watchos,
+ .mesa3d,
+ .contiki,
+ .amdpal,
+ .hermit,
+ .hurd,
+ .wasi,
+ .emscripten,
+ .uefi,
+ .other,
+ => try os_builtin_str_buffer.append(" .none = {} }\n"),
+
+ .freebsd,
+ .macosx,
+ .netbsd,
+ .openbsd,
+ => try os_builtin_str_buffer.print(
+ \\ .semver = .{{
+ \\ .min = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ .max = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ }}}},
+ \\
+ , .{
+ target.os.version_range.semver.min.major,
+ target.os.version_range.semver.min.minor,
+ target.os.version_range.semver.min.patch,
+
+ target.os.version_range.semver.max.major,
+ target.os.version_range.semver.max.minor,
+ target.os.version_range.semver.max.patch,
+ }),
+
+ .linux => try os_builtin_str_buffer.print(
+ \\ .linux = .{{
+ \\ .range = .{{
+ \\ .min = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ .max = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ }},
+ \\ .glibc = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ }}}},
+ \\
+ , .{
+ target.os.version_range.linux.range.min.major,
+ target.os.version_range.linux.range.min.minor,
+ target.os.version_range.linux.range.min.patch,
+
+ target.os.version_range.linux.range.max.major,
+ target.os.version_range.linux.range.max.minor,
+ target.os.version_range.linux.range.max.patch,
+
+ target.os.version_range.linux.glibc.major,
+ target.os.version_range.linux.glibc.minor,
+ target.os.version_range.linux.glibc.patch,
+ }),
+
+ .windows => try os_builtin_str_buffer.print(
+ \\ .windows = .{{
+ \\ .min = .{},
+ \\ .max = .{},
+ \\ }}}},
+ \\
+ , .{
+ @tagName(target.os.version_range.windows.min),
+ @tagName(target.os.version_range.windows.max),
+ }),
+ }
+ try os_builtin_str_buffer.append("};\n");
+
+ try cache_hash.append(
+ os_builtin_str_buffer.toSlice()[os_builtin_str_ver_start_index..os_builtin_str_buffer.len()],
+ );
+
+ const glibc_or_darwin_version = blk: {
+ if (target.isGnuLibC()) {
+ const stage1_glibc = try std.heap.c_allocator.create(Stage2SemVer);
+ const stage2_glibc = target.os.version_range.linux.glibc;
+ stage1_glibc.* = .{
+ .major = stage2_glibc.major,
+ .minor = stage2_glibc.minor,
+ .patch = stage2_glibc.patch,
+ };
+ break :blk stage1_glibc;
+ } else if (target.isDarwin()) {
+ const stage1_semver = try std.heap.c_allocator.create(Stage2SemVer);
+ const stage2_semver = target.os.version_range.semver.min;
+ stage1_semver.* = .{
+ .major = stage2_semver.major,
+ .minor = stage2_semver.minor,
+ .patch = stage2_semver.patch,
+ };
+ break :blk stage1_semver;
+ } else {
+ break :blk null;
+ }
};
+
self.* = .{
- .arch = @enumToInt(target.getArch()) + 1, // skip over ZigLLVM_UnknownArch
+ .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch
.vendor = 0,
- .os = @enumToInt(target.getOs()),
- .abi = @enumToInt(target.getAbi()),
- .llvm_cpu_name = null,
- .llvm_cpu_features = null,
- .builtin_str = null,
- .cache_hash = null,
- .is_native = target == .Native,
- .glibc_version = null,
+ .os = @enumToInt(target.os.tag),
+ .abi = @enumToInt(target.abi),
+ .llvm_cpu_name = if (target.cpu.model.llvm_name) |s| s.ptr else null,
+ .llvm_cpu_features = llvm_features_buffer.toOwnedSlice().ptr,
+ .cpu_builtin_str = cpu_builtin_str_buffer.toOwnedSlice().ptr,
+ .os_builtin_str = os_builtin_str_buffer.toOwnedSlice().ptr,
+ .cache_hash = cache_hash.toOwnedSlice().ptr,
+ .is_native = cross_target.isNative(),
+ .glibc_or_darwin_version = glibc_or_darwin_version,
+ .dynamic_linker = dynamic_linker,
};
- try initStage1TargetCpuFeatures(self, cpu);
}
};
-// ABI warning
-const Stage2GLibCVersion = extern struct {
- major: u32,
- minor: u32,
- patch: u32,
-};
-
-// ABI warning
-export fn stage2_detect_dynamic_linker(in_target: *const Stage2Target, out_ptr: *[*:0]u8, out_len: *usize) Error {
- const target = in_target.toTarget();
- const result = @import("introspect.zig").detectDynamicLinker(
- std.heap.c_allocator,
- target,
- ) catch |err| switch (err) {
- error.OutOfMemory => return .OutOfMemory,
- error.UnknownDynamicLinkerPath => return .UnknownDynamicLinkerPath,
- error.TargetHasNoDynamicLinker => return .TargetHasNoDynamicLinker,
- };
- out_ptr.* = result.ptr;
- out_len.* = result.len;
- return .None;
-}
-
fn enumInt(comptime Enum: type, int: c_int) Enum {
return @intToEnum(Enum, @intCast(@TagType(Enum), int));
}
+fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target {
+ var info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target);
+ if (cross_target.cpu_arch == null or cross_target.cpu_model == .native) {
+ // TODO We want to just use detected_info.target but implementing
+ // CPU model & feature detection is todo so here we rely on LLVM.
+ const llvm = @import("llvm.zig");
+ const llvm_cpu_name = llvm.GetHostCPUName();
+ const llvm_cpu_features = llvm.GetNativeFeatures();
+ const arch = std.Target.current.cpu.arch;
+ info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features);
+ cross_target.updateCpuFeatures(&info.target.cpu.features);
+ info.target.cpu.arch = cross_target.getCpuArch();
+ }
+ if (info.dynamic_linker.get()) |dl| {
+ dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl);
+ } else {
+ dynamic_linker_ptr.* = null;
+ }
+ return info.target;
+}
+
+// ABI warning
+const Stage2SemVer = extern struct {
+ major: u32,
+ minor: u32,
+ patch: u32,
+};
+
// ABI warning
const Stage2NativePaths = extern struct {
include_dirs_ptr: [*][*:0]u8,
diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig
index 7ba4188786..92ae0f2877 100644
--- a/src-self-hosted/translate_c.zig
+++ b/src-self-hosted/translate_c.zig
@@ -4850,7 +4850,7 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
}
const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc);
- const slice = begin_c[0..mem.len(u8, begin_c)];
+ const slice = begin_c[0..mem.len(begin_c)];
tok_list.shrink(0);
var tokenizer = std.c.Tokenizer{
diff --git a/src-self-hosted/util.zig b/src-self-hosted/util.zig
index 04c5420d26..2a7bf4d9cc 100644
--- a/src-self-hosted/util.zig
+++ b/src-self-hosted/util.zig
@@ -34,25 +34,3 @@ pub fn initializeAllTargets() void {
llvm.InitializeAllAsmPrinters();
llvm.InitializeAllAsmParsers();
}
-
-pub fn getTriple(allocator: *std.mem.Allocator, self: std.Target) !std.Buffer {
- var result = try std.Buffer.initSize(allocator, 0);
- errdefer result.deinit();
-
- // LLVM WebAssembly output support requires the target to be activated at
- // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly.
- //
- // LLVM determines the output format based on the abi suffix,
- // defaulting to an object based on the architecture. The default format in
- // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to
- // explicitly set this ourself in order for it to work.
- //
- // This is fixed in LLVM 7 and you will be able to get wasm output by
- // using the target triple `wasm32-unknown-unknown-unknown`.
- const env_name = if (self.isWasm()) "wasm" else @tagName(self.getAbi());
-
- var out = &std.io.BufferOutStream.init(&result).stream;
- try out.print("{}-unknown-{}-{}", .{ @tagName(self.getArch()), @tagName(self.getOs()), env_name });
-
- return result;
-}
diff --git a/src/all_types.hpp b/src/all_types.hpp
index c4989df1e9..620b95dccd 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -2249,14 +2249,11 @@ struct CodeGen {
bool test_is_evented;
CodeModel code_model;
- Buf *mmacosx_version_min;
- Buf *mios_version_min;
Buf *root_out_name;
Buf *test_filter;
Buf *test_name_prefix;
Buf *zig_lib_dir;
Buf *zig_std_dir;
- Buf *dynamic_linker_path;
Buf *version_script_path;
const char **llvm_argv;
@@ -3266,7 +3263,6 @@ struct IrInstSrcContainerInitList {
struct IrInstSrcContainerInitFieldsField {
Buf *name;
AstNode *source_node;
- TypeStructField *type_struct_field;
IrInstSrc *result_loc;
};
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 95b2c77129..c9b45164f2 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -1131,18 +1131,26 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent
Error err;
if (type_val->special != ConstValSpecialLazy) {
assert(type_val->special == ConstValSpecialStatic);
- if ((type_val->data.x_type->id == ZigTypeIdStruct &&
- type_val->data.x_type->data.structure.resolve_loop_flag_zero_bits) ||
- (type_val->data.x_type->id == ZigTypeIdUnion &&
- type_val->data.x_type->data.unionation.resolve_loop_flag_zero_bits) ||
- type_val->data.x_type->id == ZigTypeIdPointer)
+
+ // Self-referencing types via pointers are allowed and have non-zero size
+ ZigType *ty = type_val->data.x_type;
+ while (ty->id == ZigTypeIdPointer &&
+ !ty->data.pointer.resolve_loop_flag_zero_bits)
+ {
+ ty = ty->data.pointer.child_type;
+ }
+
+ if ((ty->id == ZigTypeIdStruct && ty->data.structure.resolve_loop_flag_zero_bits) ||
+ (ty->id == ZigTypeIdUnion && ty->data.unionation.resolve_loop_flag_zero_bits) ||
+ (ty->id == ZigTypeIdPointer && ty->data.pointer.resolve_loop_flag_zero_bits))
{
- // Does a struct/union which contains a pointer field to itself have bits? Yes.
*is_zero_bits = false;
return ErrorNone;
}
+
if ((err = type_resolve(g, type_val->data.x_type, ResolveStatusZeroBitsKnown)))
return err;
+
*is_zero_bits = (type_val->data.x_type->abi_size == 0);
return ErrorNone;
}
@@ -3955,7 +3963,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {
// TODO more validation for types that can't be used for export/extern variables
ZigType *implicit_type = nullptr;
- if (explicit_type != nullptr && explicit_type->id == ZigTypeIdInvalid) {
+ if (explicit_type != nullptr && type_is_invalid(explicit_type)) {
implicit_type = explicit_type;
} else if (var_decl->expr) {
init_value = analyze_const_value(g, tld_var->base.parent_scope, var_decl->expr, explicit_type,
@@ -4763,38 +4771,41 @@ static void analyze_fn_ir(CodeGen *g, ZigFn *fn, AstNode *return_type_node) {
if (return_err_set_type->data.error_set.infer_fn != nullptr &&
return_err_set_type->data.error_set.incomplete)
{
- ZigType *inferred_err_set_type;
+ // The inferred error set type is null if the function doesn't
+ // return any error
+ ZigType *inferred_err_set_type = nullptr;
+
if (fn->src_implicit_return_type->id == ZigTypeIdErrorSet) {
inferred_err_set_type = fn->src_implicit_return_type;
} else if (fn->src_implicit_return_type->id == ZigTypeIdErrorUnion) {
inferred_err_set_type = fn->src_implicit_return_type->data.error_union.err_set_type;
- } else {
- add_node_error(g, return_type_node,
- buf_sprintf("function with inferred error set must return at least one possible error"));
- fn->anal_state = FnAnalStateInvalid;
- return;
}
- if (inferred_err_set_type->data.error_set.infer_fn != nullptr &&
- inferred_err_set_type->data.error_set.incomplete)
- {
- if (!resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) {
- fn->anal_state = FnAnalStateInvalid;
- return;
- }
- }
-
- return_err_set_type->data.error_set.incomplete = false;
- if (type_is_global_error_set(inferred_err_set_type)) {
- return_err_set_type->data.error_set.err_count = UINT32_MAX;
- } else {
- return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count;
- if (inferred_err_set_type->data.error_set.err_count > 0) {
- return_err_set_type->data.error_set.errors = heap::c_allocator.allocate(inferred_err_set_type->data.error_set.err_count);
- for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) {
- return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i];
+ if (inferred_err_set_type != nullptr) {
+ if (inferred_err_set_type->data.error_set.infer_fn != nullptr &&
+ inferred_err_set_type->data.error_set.incomplete)
+ {
+ if (!resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) {
+ fn->anal_state = FnAnalStateInvalid;
+ return;
}
}
+
+ return_err_set_type->data.error_set.incomplete = false;
+ if (type_is_global_error_set(inferred_err_set_type)) {
+ return_err_set_type->data.error_set.err_count = UINT32_MAX;
+ } else {
+ return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count;
+ if (inferred_err_set_type->data.error_set.err_count > 0) {
+ return_err_set_type->data.error_set.errors = heap::c_allocator.allocate(inferred_err_set_type->data.error_set.err_count);
+ for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) {
+ return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i];
+ }
+ }
+ }
+ } else {
+ return_err_set_type->data.error_set.incomplete = false;
+ return_err_set_type->data.error_set.err_count = 0;
}
}
}
@@ -5390,6 +5401,8 @@ bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) {
static bool can_mutate_comptime_var_state(ZigValue *value) {
assert(value != nullptr);
+ if (value->special == ConstValSpecialUndef)
+ return false;
switch (value->type->id) {
case ZigTypeIdInvalid:
zig_unreachable();
@@ -5418,6 +5431,8 @@ static bool can_mutate_comptime_var_state(ZigValue *value) {
return value->data.x_ptr.mut == ConstPtrMutComptimeVar;
case ZigTypeIdArray:
+ if (value->special == ConstValSpecialUndef)
+ return false;
if (value->type->data.array.len == 0)
return false;
switch (value->data.x_array.special) {
@@ -6690,8 +6705,16 @@ bool const_values_equal_ptr(ZigValue *a, ZigValue *b) {
}
static bool const_values_equal_array(CodeGen *g, ZigValue *a, ZigValue *b, size_t len) {
- assert(a->data.x_array.special != ConstArraySpecialUndef);
- assert(b->data.x_array.special != ConstArraySpecialUndef);
+ if (a->data.x_array.special == ConstArraySpecialUndef &&
+ b->data.x_array.special == ConstArraySpecialUndef)
+ {
+ return true;
+ }
+ if (a->data.x_array.special == ConstArraySpecialUndef ||
+ b->data.x_array.special == ConstArraySpecialUndef)
+ {
+ return false;
+ }
if (a->data.x_array.special == ConstArraySpecialBuf &&
b->data.x_array.special == ConstArraySpecialBuf)
{
@@ -6713,8 +6736,6 @@ static bool const_values_equal_array(CodeGen *g, ZigValue *a, ZigValue *b, size_
bool const_values_equal(CodeGen *g, ZigValue *a, ZigValue *b) {
if (a->type->id != b->type->id) return false;
- assert(a->special == ConstValSpecialStatic);
- assert(b->special == ConstValSpecialStatic);
if (a->type == b->type) {
switch (type_has_one_possible_value(g, a->type)) {
case OnePossibleValueInvalid:
@@ -6725,6 +6746,11 @@ bool const_values_equal(CodeGen *g, ZigValue *a, ZigValue *b) {
return true;
}
}
+ if (a->special == ConstValSpecialUndef || b->special == ConstValSpecialUndef) {
+ return a->special == b->special;
+ }
+ assert(a->special == ConstValSpecialStatic);
+ assert(b->special == ConstValSpecialStatic);
switch (a->type->id) {
case ZigTypeIdOpaque:
zig_unreachable();
@@ -8708,7 +8734,6 @@ static void resolve_llvm_types_optional(CodeGen *g, ZigType *type, ResolveStatus
if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
}
- LLVMTypeRef child_llvm_type = get_llvm_type(g, child_type);
ZigLLVMDIType *child_llvm_di_type = get_llvm_di_type(g, child_type);
if (type->data.maybe.resolve_status >= wanted_resolve_status) return;
@@ -8718,35 +8743,28 @@ static void resolve_llvm_types_optional(CodeGen *g, ZigType *type, ResolveStatus
};
LLVMStructSetBody(type->llvm_type, elem_types, 2, false);
- uint64_t val_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_llvm_type);
- uint64_t val_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_llvm_type);
- uint64_t val_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 0);
+ uint64_t val_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, maybe_child_index);
+ uint64_t maybe_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, maybe_null_index);
- uint64_t maybe_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, bool_llvm_type);
- uint64_t maybe_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, bool_llvm_type);
- uint64_t maybe_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 1);
-
- uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, type->llvm_type);
- uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, type->llvm_type);
-
- ZigLLVMDIType *di_element_types[] = {
+ ZigLLVMDIType *di_element_types[2];
+ di_element_types[maybe_child_index] =
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
"val", di_file, line,
- val_debug_size_in_bits,
- val_debug_align_in_bits,
+ 8 * child_type->abi_size,
+ 8 * child_type->abi_align,
val_offset_in_bits,
- ZigLLVM_DIFlags_Zero, child_llvm_di_type),
+ ZigLLVM_DIFlags_Zero, child_llvm_di_type);
+ di_element_types[maybe_null_index] =
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
"maybe", di_file, line,
- maybe_debug_size_in_bits,
- maybe_debug_align_in_bits,
+ 8*g->builtin_types.entry_bool->abi_size,
+ 8*g->builtin_types.entry_bool->abi_align,
maybe_offset_in_bits,
- ZigLLVM_DIFlags_Zero, bool_llvm_di_type),
- };
+ ZigLLVM_DIFlags_Zero, bool_llvm_di_type);
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
compile_unit_scope,
buf_ptr(&type->name),
- di_file, line, debug_size_in_bits, debug_align_in_bits, ZigLLVM_DIFlags_Zero,
+ di_file, line, 8 * type->abi_size, 8 * type->abi_align, ZigLLVM_DIFlags_Zero,
nullptr, di_element_types, 2, 0, nullptr, "");
ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
@@ -9387,13 +9405,24 @@ void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src) {
dest->data.x_struct.fields[i]->parent.data.p_struct.field_index = i;
}
} else if (dest->type->id == ZigTypeIdArray) {
- if (dest->data.x_array.special == ConstArraySpecialNone) {
- dest->data.x_array.data.s_none.elements = g->pass1_arena->allocate(dest->type->data.array.len);
- for (uint64_t i = 0; i < dest->type->data.array.len; i += 1) {
- copy_const_val(g, &dest->data.x_array.data.s_none.elements[i], &src->data.x_array.data.s_none.elements[i]);
- dest->data.x_array.data.s_none.elements[i].parent.id = ConstParentIdArray;
- dest->data.x_array.data.s_none.elements[i].parent.data.p_array.array_val = dest;
- dest->data.x_array.data.s_none.elements[i].parent.data.p_array.elem_index = i;
+ switch (dest->data.x_array.special) {
+ case ConstArraySpecialNone: {
+ dest->data.x_array.data.s_none.elements = g->pass1_arena->allocate(dest->type->data.array.len);
+ for (uint64_t i = 0; i < dest->type->data.array.len; i += 1) {
+ copy_const_val(g, &dest->data.x_array.data.s_none.elements[i], &src->data.x_array.data.s_none.elements[i]);
+ dest->data.x_array.data.s_none.elements[i].parent.id = ConstParentIdArray;
+ dest->data.x_array.data.s_none.elements[i].parent.data.p_array.array_val = dest;
+ dest->data.x_array.data.s_none.elements[i].parent.data.p_array.elem_index = i;
+ }
+ break;
+ }
+ case ConstArraySpecialUndef: {
+ // Nothing to copy; the above memcpy did everything we needed.
+ break;
+ }
+ case ConstArraySpecialBuf: {
+ dest->data.x_array.data.s_buf = buf_create_from_buf(src->data.x_array.data.s_buf);
+ break;
}
}
} else if (type_has_optional_repr(dest->type) && dest->data.x_optional != nullptr) {
diff --git a/src/codegen.cpp b/src/codegen.cpp
index dd55f3a665..55a9fa67ff 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -32,31 +32,6 @@ enum ResumeId {
ResumeIdCall,
};
-static void init_darwin_native(CodeGen *g) {
- char *osx_target = getenv("MACOSX_DEPLOYMENT_TARGET");
- char *ios_target = getenv("IPHONEOS_DEPLOYMENT_TARGET");
-
- // Allow conflicts among OSX and iOS, but choose the default platform.
- if (osx_target && ios_target) {
- if (g->zig_target->arch == ZigLLVM_arm ||
- g->zig_target->arch == ZigLLVM_aarch64 ||
- g->zig_target->arch == ZigLLVM_thumb)
- {
- osx_target = nullptr;
- } else {
- ios_target = nullptr;
- }
- }
-
- if (osx_target) {
- g->mmacosx_version_min = buf_create_from_str(osx_target);
- } else if (ios_target) {
- g->mios_version_min = buf_create_from_str(ios_target);
- } else if (g->zig_target->os != OsIOS) {
- g->mmacosx_version_min = buf_create_from_str("10.14");
- }
-}
-
static ZigPackage *new_package(const char *root_src_dir, const char *root_src_path, const char *pkg_path) {
ZigPackage *entry = heap::c_allocator.create();
entry->package_table.init(4);
@@ -160,14 +135,6 @@ void codegen_add_framework(CodeGen *g, const char *framework) {
g->darwin_frameworks.append(buf_create_from_str(framework));
}
-void codegen_set_mmacosx_version_min(CodeGen *g, Buf *mmacosx_version_min) {
- g->mmacosx_version_min = mmacosx_version_min;
-}
-
-void codegen_set_mios_version_min(CodeGen *g, Buf *mios_version_min) {
- g->mios_version_min = mios_version_min;
-}
-
void codegen_set_rdynamic(CodeGen *g, bool rdynamic) {
g->linker_rdynamic = rdynamic;
}
@@ -972,7 +939,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
case PanicMsgIdExactDivisionRemainder:
return buf_create_from_str("exact division produced remainder");
case PanicMsgIdUnwrapOptionalFail:
- return buf_create_from_str("attempt to unwrap null");
+ return buf_create_from_str("attempt to use null value");
case PanicMsgIdUnreachable:
return buf_create_from_str("reached unreachable code");
case PanicMsgIdInvalidErrorCode:
@@ -3325,11 +3292,24 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutableGen *executabl
LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue");
size_t field_count = wanted_type->data.enumeration.src_field_count;
LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count);
+
+ HashMap occupied_tag_values = {};
+ occupied_tag_values.init(field_count);
+
for (size_t field_i = 0; field_i < field_count; field_i += 1) {
+ TypeEnumField *type_enum_field = &wanted_type->data.enumeration.fields[field_i];
+
+ Buf *name = type_enum_field->name;
+ auto entry = occupied_tag_values.put_unique(type_enum_field->value, name);
+ if (entry != nullptr) {
+ continue;
+ }
+
LLVMValueRef this_tag_int_value = bigint_to_llvm_const(get_llvm_type(g, tag_int_type),
- &wanted_type->data.enumeration.fields[field_i].value);
+ &type_enum_field->value);
LLVMAddCase(switch_instr, this_tag_int_value, ok_value_block);
}
+ occupied_tag_values.deinit();
LLVMPositionBuilderAtEnd(g->builder, bad_value_block);
gen_safety_crash(g, PanicMsgIdBadEnumValue);
@@ -4466,7 +4446,7 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutableGen *execu
if (!type_has_bits(field->type_entry)) {
ZigType *tag_type = union_type->data.unionation.tag_type;
- if (!instruction->initializing || !type_has_bits(tag_type))
+ if (!instruction->initializing || tag_type == nullptr || !type_has_bits(tag_type))
return nullptr;
// The field has no bits but we still have to change the discriminant
@@ -5026,8 +5006,18 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) {
LLVMConstNull(usize->llvm_type),
};
+ HashMap occupied_tag_values = {};
+ occupied_tag_values.init(field_count);
+
for (size_t field_i = 0; field_i < field_count; field_i += 1) {
- Buf *name = enum_type->data.enumeration.fields[field_i].name;
+ TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
+
+ Buf *name = type_enum_field->name;
+ auto entry = occupied_tag_values.put_unique(type_enum_field->value, name);
+ if (entry != nullptr) {
+ continue;
+ }
+
LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true);
LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), "");
LLVMSetInitializer(str_global, str_init);
@@ -5057,6 +5047,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) {
LLVMPositionBuilderAtEnd(g->builder, return_block);
LLVMBuildRet(g->builder, slice_global);
}
+ occupied_tag_values.deinit();
LLVMPositionBuilderAtEnd(g->builder, bad_value_block);
if (g->build_mode == BuildModeDebug || g->build_mode == BuildModeSafeRelease) {
@@ -5081,11 +5072,6 @@ static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutableGen *executa
{
ZigType *enum_type = instruction->target->value->type;
assert(enum_type->id == ZigTypeIdEnum);
- if (enum_type->data.enumeration.non_exhaustive) {
- add_node_error(g, instruction->base.base.source_node,
- buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991"));
- codegen_report_errors_and_exit(g);
- }
LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type);
@@ -8518,25 +8504,24 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
buf_appendf(contents, "pub const link_mode = LinkMode.%s;\n", link_type);
buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build));
buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded));
- buf_appendf(contents, "pub const os = Os.%s;\n", cur_os);
+ buf_append_str(contents, "/// Deprecated: use `std.Target.cpu.arch`\n");
buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch);
buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi);
{
buf_append_str(contents, "pub const cpu: Cpu = ");
- if (g->zig_target->builtin_str != nullptr) {
- buf_append_str(contents, g->zig_target->builtin_str);
+ if (g->zig_target->cpu_builtin_str != nullptr) {
+ buf_append_str(contents, g->zig_target->cpu_builtin_str);
} else {
- buf_append_str(contents, "Target.Cpu.baseline(arch);\n");
+ buf_appendf(contents, "Target.Cpu.baseline(.%s);\n", cur_arch);
}
}
- if (g->libc_link_lib != nullptr && g->zig_target->glibc_version != nullptr) {
- buf_appendf(contents,
- "pub const glibc_version: ?Version = Version{.major = %d, .minor = %d, .patch = %d};\n",
- g->zig_target->glibc_version->major,
- g->zig_target->glibc_version->minor,
- g->zig_target->glibc_version->patch);
- } else {
- buf_appendf(contents, "pub const glibc_version: ?Version = null;\n");
+ {
+ buf_append_str(contents, "pub const os = ");
+ if (g->zig_target->os_builtin_str != nullptr) {
+ buf_append_str(contents, g->zig_target->os_builtin_str);
+ } else {
+ buf_appendf(contents, "Target.Os.defaultVersionRange(.%s);\n", cur_os);
+ }
}
buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt);
buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode));
@@ -8631,10 +8616,10 @@ static Error define_builtin_compile_vars(CodeGen *g) {
if (g->zig_target->cache_hash != nullptr) {
cache_str(&cache_hash, g->zig_target->cache_hash);
}
- if (g->zig_target->glibc_version != nullptr) {
- cache_int(&cache_hash, g->zig_target->glibc_version->major);
- cache_int(&cache_hash, g->zig_target->glibc_version->minor);
- cache_int(&cache_hash, g->zig_target->glibc_version->patch);
+ if (g->zig_target->glibc_or_darwin_version != nullptr) {
+ cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->major);
+ cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->minor);
+ cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->patch);
}
cache_bool(&cache_hash, g->have_err_ret_tracing);
cache_bool(&cache_hash, g->libc_link_lib != nullptr);
@@ -8841,28 +8826,6 @@ static void init(CodeGen *g) {
}
}
-static void detect_dynamic_linker(CodeGen *g) {
- Error err;
-
- if (g->dynamic_linker_path != nullptr)
- return;
- if (!g->have_dynamic_link)
- return;
- if (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic))
- return;
-
- char *dynamic_linker_ptr;
- size_t dynamic_linker_len;
- if ((err = stage2_detect_dynamic_linker(g->zig_target, &dynamic_linker_ptr, &dynamic_linker_len))) {
- if (err == ErrorTargetHasNoDynamicLinker) return;
- fprintf(stderr, "Unable to detect dynamic linker: %s\n", err_str(err));
- exit(1);
- }
- g->dynamic_linker_path = buf_create_from_mem(dynamic_linker_ptr, dynamic_linker_len);
- // Skips heap::c_allocator because the memory is allocated by stage2 library.
- free(dynamic_linker_ptr);
-}
-
static void detect_libc(CodeGen *g) {
Error err;
@@ -10298,10 +10261,13 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
if (g->zig_target->cache_hash != nullptr) {
cache_str(ch, g->zig_target->cache_hash);
}
- if (g->zig_target->glibc_version != nullptr) {
- cache_int(ch, g->zig_target->glibc_version->major);
- cache_int(ch, g->zig_target->glibc_version->minor);
- cache_int(ch, g->zig_target->glibc_version->patch);
+ if (g->zig_target->glibc_or_darwin_version != nullptr) {
+ cache_int(ch, g->zig_target->glibc_or_darwin_version->major);
+ cache_int(ch, g->zig_target->glibc_or_darwin_version->minor);
+ cache_int(ch, g->zig_target->glibc_or_darwin_version->patch);
+ }
+ if (g->zig_target->dynamic_linker != nullptr) {
+ cache_str(ch, g->zig_target->dynamic_linker);
}
cache_int(ch, detect_subsystem(g));
cache_bool(ch, g->strip_debug_symbols);
@@ -10329,8 +10295,6 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
cache_bool(ch, g->emit_bin);
cache_bool(ch, g->emit_llvm_ir);
cache_bool(ch, g->emit_asm);
- cache_buf_opt(ch, g->mmacosx_version_min);
- cache_buf_opt(ch, g->mios_version_min);
cache_usize(ch, g->version_major);
cache_usize(ch, g->version_minor);
cache_usize(ch, g->version_patch);
@@ -10345,7 +10309,6 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
cache_str(ch, g->libc->msvc_lib_dir);
cache_str(ch, g->libc->kernel32_lib_dir);
}
- cache_buf_opt(ch, g->dynamic_linker_path);
cache_buf_opt(ch, g->version_script_path);
// gen_c_objects appends objects to g->link_objects which we want to include in the hash
@@ -10442,7 +10405,6 @@ void codegen_build_and_link(CodeGen *g) {
g->have_err_ret_tracing = detect_err_ret_tracing(g);
g->have_sanitize_c = detect_sanitize_c(g);
detect_libc(g);
- detect_dynamic_linker(g);
Buf digest = BUF_INIT;
if (g->enable_cache) {
@@ -10639,7 +10601,6 @@ CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType o
child_gen->verbose_cc = parent_gen->verbose_cc;
child_gen->verbose_llvm_cpu_features = parent_gen->verbose_llvm_cpu_features;
child_gen->llvm_argv = parent_gen->llvm_argv;
- child_gen->dynamic_linker_path = parent_gen->dynamic_linker_path;
codegen_set_strip(child_gen, parent_gen->strip_debug_symbols);
child_gen->want_pic = parent_gen->have_pic ? WantPICEnabled : WantPICDisabled;
@@ -10647,9 +10608,6 @@ CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType o
codegen_set_errmsg_color(child_gen, parent_gen->err_color);
- codegen_set_mmacosx_version_min(child_gen, parent_gen->mmacosx_version_min);
- codegen_set_mios_version_min(child_gen, parent_gen->mios_version_min);
-
child_gen->enable_cache = true;
return child_gen;
@@ -10757,11 +10715,6 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget
g->each_lib_rpath = false;
} else {
g->each_lib_rpath = true;
-
- if (target_os_is_darwin(g->zig_target->os)) {
- init_darwin_native(g);
- }
-
}
if (target_os_requires_libc(g->zig_target->os)) {
diff --git a/src/codegen.hpp b/src/codegen.hpp
index 6329c59a5e..191da9a04b 100644
--- a/src/codegen.hpp
+++ b/src/codegen.hpp
@@ -35,8 +35,6 @@ LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib);
void codegen_add_framework(CodeGen *codegen, const char *name);
void codegen_add_rpath(CodeGen *codegen, const char *name);
void codegen_set_rdynamic(CodeGen *g, bool rdynamic);
-void codegen_set_mmacosx_version_min(CodeGen *g, Buf *mmacosx_version_min);
-void codegen_set_mios_version_min(CodeGen *g, Buf *mios_version_min);
void codegen_set_linker_script(CodeGen *g, const char *linker_script);
void codegen_set_test_filter(CodeGen *g, Buf *filter);
void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix);
diff --git a/src/compiler.cpp b/src/compiler.cpp
index 31bac4ee24..cddecc2025 100644
--- a/src/compiler.cpp
+++ b/src/compiler.cpp
@@ -4,31 +4,6 @@
#include
-Buf *get_self_libc_path(void) {
- static Buf saved_libc_path = BUF_INIT;
- static bool searched_for_libc = false;
-
- for (;;) {
- if (saved_libc_path.list.length != 0) {
- return &saved_libc_path;
- }
- if (searched_for_libc)
- return nullptr;
- ZigList lib_paths = {};
- Error err;
- if ((err = os_self_exe_shared_libs(lib_paths)))
- return nullptr;
- for (size_t i = 0; i < lib_paths.length; i += 1) {
- Buf *lib_path = lib_paths.at(i);
- if (buf_ends_with_str(lib_path, "libc.so.6")) {
- buf_init_from_buf(&saved_libc_path, lib_path);
- return &saved_libc_path;
- }
- }
- searched_for_libc = true;
- }
-}
-
Error get_compiler_id(Buf **result) {
static Buf saved_compiler_id = BUF_INIT;
diff --git a/src/compiler.hpp b/src/compiler.hpp
index 4a1699b782..47841af5dc 100644
--- a/src/compiler.hpp
+++ b/src/compiler.hpp
@@ -12,7 +12,6 @@
#include "error.hpp"
Error get_compiler_id(Buf **result);
-Buf *get_self_libc_path(void);
Buf *get_zig_lib_dir(void);
Buf *get_zig_special_dir(Buf *zig_lib_dir);
diff --git a/src/error.cpp b/src/error.cpp
index 730c6e7193..2e92a98217 100644
--- a/src/error.cpp
+++ b/src/error.cpp
@@ -81,6 +81,8 @@ const char *err_str(Error err) {
case ErrorWindowsSdkNotFound: return "Windows SDK not found";
case ErrorUnknownDynamicLinkerPath: return "unknown dynamic linker path";
case ErrorTargetHasNoDynamicLinker: return "target has no dynamic linker";
+ case ErrorInvalidAbiVersion: return "invalid C ABI version";
+ case ErrorInvalidOperatingSystemVersion: return "invalid operating system version";
}
return "(invalid error)";
}
diff --git a/src/glibc.cpp b/src/glibc.cpp
index 849aac6c77..da5c1d5290 100644
--- a/src/glibc.cpp
+++ b/src/glibc.cpp
@@ -55,7 +55,7 @@ Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbo
Optional> opt_component = SplitIterator_next(&it);
if (!opt_component.is_some) break;
Buf *ver_buf = buf_create_from_slice(opt_component.value);
- ZigGLibCVersion *this_ver = glibc_abi->all_versions.add_one();
+ Stage2SemVer *this_ver = glibc_abi->all_versions.add_one();
if ((err = target_parse_glibc_version(this_ver, buf_ptr(ver_buf)))) {
if (verbose) {
fprintf(stderr, "Unable to parse glibc version '%s': %s\n", buf_ptr(ver_buf), err_str(err));
@@ -186,9 +186,9 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con
cache_buf(cache_hash, compiler_id);
cache_int(cache_hash, target->arch);
cache_int(cache_hash, target->abi);
- cache_int(cache_hash, target->glibc_version->major);
- cache_int(cache_hash, target->glibc_version->minor);
- cache_int(cache_hash, target->glibc_version->patch);
+ cache_int(cache_hash, target->glibc_or_darwin_version->major);
+ cache_int(cache_hash, target->glibc_or_darwin_version->minor);
+ cache_int(cache_hash, target->glibc_or_darwin_version->patch);
Buf digest = BUF_INIT;
buf_resize(&digest, 0);
@@ -224,10 +224,10 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con
uint8_t target_ver_index = 0;
for (;target_ver_index < glibc_abi->all_versions.length; target_ver_index += 1) {
- const ZigGLibCVersion *this_ver = &glibc_abi->all_versions.at(target_ver_index);
- if (this_ver->major == target->glibc_version->major &&
- this_ver->minor == target->glibc_version->minor &&
- this_ver->patch == target->glibc_version->patch)
+ const Stage2SemVer *this_ver = &glibc_abi->all_versions.at(target_ver_index);
+ if (this_ver->major == target->glibc_or_darwin_version->major &&
+ this_ver->minor == target->glibc_or_darwin_version->minor &&
+ this_ver->patch == target->glibc_or_darwin_version->patch)
{
break;
}
@@ -235,9 +235,9 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con
if (target_ver_index == glibc_abi->all_versions.length) {
if (verbose) {
fprintf(stderr, "Unrecognized glibc version: %d.%d.%d\n",
- target->glibc_version->major,
- target->glibc_version->minor,
- target->glibc_version->patch);
+ target->glibc_or_darwin_version->major,
+ target->glibc_or_darwin_version->minor,
+ target->glibc_or_darwin_version->patch);
}
return ErrorUnknownABI;
}
@@ -246,7 +246,7 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con
Buf *map_contents = buf_alloc();
for (uint8_t ver_i = 0; ver_i < glibc_abi->all_versions.length; ver_i += 1) {
- const ZigGLibCVersion *ver = &glibc_abi->all_versions.at(ver_i);
+ const Stage2SemVer *ver = &glibc_abi->all_versions.at(ver_i);
if (ver->patch == 0) {
buf_appendf(map_contents, "GLIBC_%d.%d { };\n", ver->major, ver->minor);
} else {
@@ -294,7 +294,7 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con
uint8_t ver_index = ver_list->versions[ver_i];
Buf *stub_name;
- const ZigGLibCVersion *ver = &glibc_abi->all_versions.at(ver_index);
+ const Stage2SemVer *ver = &glibc_abi->all_versions.at(ver_index);
const char *sym_name = buf_ptr(libc_fn->name);
if (ver->patch == 0) {
stub_name = buf_sprintf("%s_%d_%d", sym_name, ver->major, ver->minor);
@@ -362,43 +362,6 @@ bool eql_glibc_target(const ZigTarget *a, const ZigTarget *b) {
a->abi == b->abi;
}
-#ifdef ZIG_OS_LINUX
-#include
-Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver) {
- Buf *self_libc_path = get_self_libc_path();
- if (self_libc_path == nullptr) {
- // TODO There is still more we could do to detect the native glibc version. For example,
- // we could look at the ELF file of `/usr/bin/env`, find `libc.so.6`, and then `readlink`
- // to find out the glibc version. This is relevant for the static zig builds distributed
- // on the download page, since the above detection based on zig's own dynamic linking
- // will not work.
-
- return ErrorUnknownABI;
- }
- Buf *link_name = buf_alloc();
- buf_resize(link_name, 4096);
- ssize_t amt = readlink(buf_ptr(self_libc_path), buf_ptr(link_name), buf_len(link_name));
- if (amt == -1) {
- return ErrorUnknownABI;
- }
- buf_resize(link_name, amt);
- if (!buf_starts_with_str(link_name, "libc-") || !buf_ends_with_str(link_name, ".so")) {
- return ErrorUnknownABI;
- }
- // example: "libc-2.3.4.so"
- // example: "libc-2.27.so"
- buf_resize(link_name, buf_len(link_name) - 3); // chop off ".so"
- glibc_ver->major = 2;
- glibc_ver->minor = 0;
- glibc_ver->patch = 0;
- return target_parse_glibc_version(glibc_ver, buf_ptr(link_name) + 5);
-}
-#else
-Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver) {
- return ErrorUnknownABI;
-}
-#endif
-
size_t glibc_lib_count(void) {
return array_length(glibc_libs);
}
diff --git a/src/glibc.hpp b/src/glibc.hpp
index 42c2099371..c04dcb4629 100644
--- a/src/glibc.hpp
+++ b/src/glibc.hpp
@@ -32,7 +32,7 @@ struct ZigGLibCAbi {
Buf *abi_txt_path;
Buf *vers_txt_path;
Buf *fns_txt_path;
- ZigList all_versions;
+ ZigList all_versions;
ZigList all_functions;
// The value is a pointer to all_functions.length items and each item is an index
// into all_functions.
@@ -43,9 +43,6 @@ Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbo
Error glibc_build_dummies_and_maps(CodeGen *codegen, const ZigGLibCAbi *glibc_abi, const ZigTarget *target,
Buf **out_dir, bool verbose, Stage2ProgressNode *progress_node);
-// returns ErrorUnknownABI when glibc is not the native libc
-Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver);
-
size_t glibc_lib_count(void);
const ZigGLibCLib *glibc_lib_enum(size_t index);
const ZigGLibCLib *glibc_lib_find(const char *name);
diff --git a/src/ir.cpp b/src/ir.cpp
index 0e276a8cc7..a2cc68b3ca 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -12555,13 +12555,22 @@ static IrInstGen *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInst* sourc
{
Error err;
- if ((err = type_resolve(ira->codegen, array_ptr->value->type->data.pointer.child_type,
- ResolveStatusAlignmentKnown)))
- {
+ assert(array_ptr->value->type->id == ZigTypeIdPointer);
+
+ if ((err = type_resolve(ira->codegen, array_ptr->value->type, ResolveStatusAlignmentKnown))) {
return ira->codegen->invalid_inst_gen;
}
- wanted_type = adjust_slice_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, array_ptr->value->type));
+ assert(array_ptr->value->type->data.pointer.child_type->id == ZigTypeIdArray);
+
+ const size_t array_len = array_ptr->value->type->data.pointer.child_type->data.array.len;
+
+ // A zero-sized array can always be casted irregardless of the destination
+ // alignment
+ if (array_len != 0) {
+ wanted_type = adjust_slice_align(ira->codegen, wanted_type,
+ get_ptr_align(ira->codegen, array_ptr->value->type));
+ }
if (instr_is_comptime(array_ptr)) {
ZigValue *array_ptr_val = ir_resolve_const(ira, array_ptr, UndefBad);
@@ -14833,19 +14842,19 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
// cast from inferred struct type to array, union, or struct
if (is_anon_container(actual_type)) {
- AstNode *decl_node = actual_type->data.structure.decl_node;
- ir_assert(decl_node->type == NodeTypeContainerInitExpr, source_instr);
- ContainerInitKind init_kind = decl_node->data.container_init_expr.kind;
- uint32_t field_count = actual_type->data.structure.src_field_count;
- if (wanted_type->id == ZigTypeIdArray && (init_kind == ContainerInitKindArray || field_count == 0) &&
+ const bool is_array_init =
+ actual_type->data.structure.special == StructSpecialInferredTuple;
+ const uint32_t field_count = actual_type->data.structure.src_field_count;
+
+ if (wanted_type->id == ZigTypeIdArray && (is_array_init || field_count == 0) &&
wanted_type->data.array.len == field_count)
{
return ir_analyze_struct_literal_to_array(ira, source_instr, value, wanted_type);
} else if (wanted_type->id == ZigTypeIdStruct &&
- (init_kind == ContainerInitKindStruct || field_count == 0))
+ (!is_array_init || field_count == 0))
{
return ir_analyze_struct_literal_to_struct(ira, source_instr, value, wanted_type);
- } else if (wanted_type->id == ZigTypeIdUnion && init_kind == ContainerInitKindStruct && field_count == 1) {
+ } else if (wanted_type->id == ZigTypeIdUnion && !is_array_init && field_count == 1) {
return ir_analyze_struct_literal_to_union(ira, source_instr, value, wanted_type);
}
}
@@ -17799,6 +17808,7 @@ static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport
}
} break;
case ZigTypeIdInt:
+ want_var_export = true;
break;
case ZigTypeIdVoid:
case ZigTypeIdBool:
@@ -20369,6 +20379,17 @@ static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) {
ptr_type->data.pointer.allow_zero);
}
+static ZigType *adjust_ptr_allow_zero(CodeGen *g, ZigType *ptr_type, bool allow_zero) {
+ assert(ptr_type->id == ZigTypeIdPointer);
+ return get_pointer_to_type_extra(g,
+ ptr_type->data.pointer.child_type,
+ ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
+ ptr_type->data.pointer.ptr_len,
+ ptr_type->data.pointer.explicit_alignment,
+ ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes,
+ allow_zero);
+}
+
static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemPtr *elem_ptr_instruction) {
Error err;
IrInstGen *array_ptr = elem_ptr_instruction->array_ptr->child;
@@ -23148,12 +23169,15 @@ static IrInstGen *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstSrc
if (instr_is_comptime(target)) {
if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_inst_gen;
- if (target->value->type->data.enumeration.non_exhaustive) {
- ir_add_error(ira, &instruction->base.base,
- buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991"));
+ TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint);
+ if (field == nullptr) {
+ Buf *int_buf = buf_alloc();
+ bigint_append_buf(int_buf, &target->value->data.x_bigint, 10);
+
+ ir_add_error(ira, &target->base,
+ buf_sprintf("no tag by value %s", buf_ptr(int_buf)));
return ira->codegen->invalid_inst_gen;
}
- TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint);
ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee;
IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr);
init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(field->name), true);
@@ -25920,6 +25944,8 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
ZigType *non_sentinel_slice_ptr_type;
ZigType *elem_type;
+ bool generate_non_null_assert = false;
+
if (array_type->id == ZigTypeIdArray) {
elem_type = array_type->data.array.child_type;
bool is_comptime_const = ptr_ptr->value->special == ConstValSpecialStatic &&
@@ -25947,6 +25973,14 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
elem_type = array_type->data.pointer.child_type;
if (array_type->data.pointer.ptr_len == PtrLenC) {
array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown);
+
+ // C pointers are allowzero by default.
+ // However, we want to be able to slice them without generating an allowzero slice (see issue #4401).
+ // To achieve this, we generate a runtime safety check and make the slice type non-allowzero.
+ if (array_type->data.pointer.allow_zero) {
+ array_type = adjust_ptr_allow_zero(ira->codegen, array_type, false);
+ generate_non_null_assert = true;
+ }
}
ZigType *maybe_sentineled_slice_ptr_type = array_type;
non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr);
@@ -26218,7 +26252,6 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc,
return_type, nullptr, true, true);
-
if (result_loc != nullptr) {
if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) {
return result_loc;
@@ -26231,8 +26264,17 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
return ira->codegen->invalid_inst_gen;
}
- return ir_build_slice_gen(ira, &instruction->base.base, return_type,
- ptr_ptr, casted_start, end, instruction->safety_check_on, result_loc);
+ if (generate_non_null_assert) {
+ IrInstGen *ptr_val = ir_get_deref(ira, &instruction->base.base, ptr_ptr, nullptr);
+
+ if (type_is_invalid(ptr_val->value->type))
+ return ira->codegen->invalid_inst_gen;
+
+ ir_build_assert_non_null(ira, &instruction->base.base, ptr_val);
+ }
+
+ return ir_build_slice_gen(ira, &instruction->base.base, return_type, ptr_ptr,
+ casted_start, end, instruction->safety_check_on, result_loc);
}
static IrInstGen *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstSrcHasField *instruction) {
@@ -27818,9 +27860,15 @@ static IrInstGen *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInst* source_instr, Ir
}
IrInstGen *result = ir_const(ira, source_instr, ptr_type);
- result->value->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
- result->value->data.x_ptr.mut = ConstPtrMutRuntimeVar;
- result->value->data.x_ptr.data.hard_coded_addr.addr = addr;
+ if (ptr_type->id == ZigTypeIdOptional && addr == 0) {
+ result->value->data.x_ptr.special = ConstPtrSpecialNull;
+ result->value->data.x_ptr.mut = ConstPtrMutComptimeConst;
+ } else {
+ result->value->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
+ result->value->data.x_ptr.mut = ConstPtrMutRuntimeVar;
+ result->value->data.x_ptr.data.hard_coded_addr.addr = addr;
+ }
+
return result;
}
@@ -27878,15 +27926,15 @@ static IrInstGen *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstSrcPtr
ZigType *usize = ira->codegen->builtin_types.entry_usize;
- // We check size explicitly so we can use get_src_ptr_type here.
- if (get_src_ptr_type(target->value->type) == nullptr) {
+ ZigType *src_ptr_type = get_src_ptr_type(target->value->type);
+ if (src_ptr_type == nullptr) {
ir_add_error(ira, &target->base,
buf_sprintf("expected pointer, found '%s'", buf_ptr(&target->value->type->name)));
return ira->codegen->invalid_inst_gen;
}
bool has_bits;
- if ((err = type_has_bits2(ira->codegen, target->value->type, &has_bits)))
+ if ((err = type_has_bits2(ira->codegen, src_ptr_type, &has_bits)))
return ira->codegen->invalid_inst_gen;
if (!has_bits) {
@@ -27899,11 +27947,19 @@ static IrInstGen *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstSrcPtr
ZigValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_inst_gen;
- if (val->type->id == ZigTypeIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
+
+ // Since we've already run this type trough get_codegen_ptr_type it is
+ // safe to access the x_ptr fields
+ if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
IrInstGen *result = ir_const(ira, &instruction->base.base, usize);
bigint_init_unsigned(&result->value->data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr);
result->value->type = usize;
return result;
+ } else if (val->data.x_ptr.special == ConstPtrSpecialNull) {
+ IrInstGen *result = ir_const(ira, &instruction->base.base, usize);
+ bigint_init_unsigned(&result->value->data.x_bigint, 0);
+ result->value->type = usize;
+ return result;
}
}
diff --git a/src/link.cpp b/src/link.cpp
index ded2ac2cce..e6d186cef9 100644
--- a/src/link.cpp
+++ b/src/link.cpp
@@ -1751,9 +1751,9 @@ static void construct_linker_job_elf(LinkJob *lj) {
}
if (g->have_dynamic_link && (is_dyn_lib || g->out_type == OutTypeExe)) {
- assert(g->dynamic_linker_path != nullptr);
+ assert(g->zig_target->dynamic_linker != nullptr);
lj->args.append("-dynamic-linker");
- lj->args.append(buf_ptr(g->dynamic_linker_path));
+ lj->args.append(g->zig_target->dynamic_linker);
}
}
@@ -2376,99 +2376,6 @@ static void construct_linker_job_coff(LinkJob *lj) {
}
}
-
-// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the
-// grouped values as integers. Numbers which are not provided are set to 0.
-// return true if the entire string was parsed (9.2), or all groups were
-// parsed (10.3.5extrastuff).
-static bool darwin_get_release_version(const char *str, int *major, int *minor, int *micro, bool *had_extra) {
- *had_extra = false;
-
- *major = 0;
- *minor = 0;
- *micro = 0;
-
- if (*str == '\0')
- return false;
-
- char *end;
- *major = (int)strtol(str, &end, 10);
- if (*str != '\0' && *end == '\0')
- return true;
- if (*end != '.')
- return false;
-
- str = end + 1;
- *minor = (int)strtol(str, &end, 10);
- if (*str != '\0' && *end == '\0')
- return true;
- if (*end != '.')
- return false;
-
- str = end + 1;
- *micro = (int)strtol(str, &end, 10);
- if (*str != '\0' && *end == '\0')
- return true;
- if (str == end)
- return false;
- *had_extra = true;
- return true;
-}
-
-enum DarwinPlatformKind {
- MacOS,
- IPhoneOS,
- IPhoneOSSimulator,
-};
-
-struct DarwinPlatform {
- DarwinPlatformKind kind;
- int major;
- int minor;
- int micro;
-};
-
-static void get_darwin_platform(LinkJob *lj, DarwinPlatform *platform) {
- CodeGen *g = lj->codegen;
-
- if (g->mmacosx_version_min) {
- platform->kind = MacOS;
- } else if (g->mios_version_min) {
- platform->kind = IPhoneOS;
- } else if (g->zig_target->os == OsMacOSX) {
- platform->kind = MacOS;
- g->mmacosx_version_min = buf_create_from_str("10.14");
- } else {
- zig_panic("unable to infer -mmacosx-version-min or -mios-version-min");
- }
-
- bool had_extra;
- if (platform->kind == MacOS) {
- if (!darwin_get_release_version(buf_ptr(g->mmacosx_version_min),
- &platform->major, &platform->minor, &platform->micro, &had_extra) ||
- had_extra || platform->major != 10 || platform->minor >= 100 || platform->micro >= 100)
- {
- zig_panic("invalid -mmacosx-version-min");
- }
- } else if (platform->kind == IPhoneOS) {
- if (!darwin_get_release_version(buf_ptr(g->mios_version_min),
- &platform->major, &platform->minor, &platform->micro, &had_extra) ||
- had_extra || platform->major >= 10 || platform->minor >= 100 || platform->micro >= 100)
- {
- zig_panic("invalid -mios-version-min");
- }
- } else {
- zig_unreachable();
- }
-
- if (platform->kind == IPhoneOS &&
- (g->zig_target->arch == ZigLLVM_x86 ||
- g->zig_target->arch == ZigLLVM_x86_64))
- {
- platform->kind = IPhoneOSSimulator;
- }
-}
-
static void construct_linker_job_macho(LinkJob *lj) {
CodeGen *g = lj->codegen;
@@ -2512,25 +2419,25 @@ static void construct_linker_job_macho(LinkJob *lj) {
lj->args.append("-arch");
lj->args.append(get_darwin_arch_string(g->zig_target));
- DarwinPlatform platform;
- get_darwin_platform(lj, &platform);
- switch (platform.kind) {
- case MacOS:
+ if (g->zig_target->glibc_or_darwin_version != nullptr) {
+ if (g->zig_target->os == OsMacOSX) {
lj->args.append("-macosx_version_min");
- break;
- case IPhoneOS:
- lj->args.append("-iphoneos_version_min");
- break;
- case IPhoneOSSimulator:
- lj->args.append("-ios_simulator_version_min");
- break;
+ } else if (g->zig_target->os == OsIOS) {
+ if (g->zig_target->arch == ZigLLVM_x86 || g->zig_target->arch == ZigLLVM_x86_64) {
+ lj->args.append("-ios_simulator_version_min");
+ } else {
+ lj->args.append("-iphoneos_version_min");
+ }
+ }
+ Buf *version_string = buf_sprintf("%d.%d.%d",
+ g->zig_target->glibc_or_darwin_version->major,
+ g->zig_target->glibc_or_darwin_version->minor,
+ g->zig_target->glibc_or_darwin_version->patch);
+ lj->args.append(buf_ptr(version_string));
+
+ lj->args.append("-sdk_version");
+ lj->args.append(buf_ptr(version_string));
}
- Buf *version_string = buf_sprintf("%d.%d.%d", platform.major, platform.minor, platform.micro);
- lj->args.append(buf_ptr(version_string));
-
- lj->args.append("-sdk_version");
- lj->args.append(buf_ptr(version_string));
-
if (g->out_type == OutTypeExe) {
lj->args.append("-pie");
diff --git a/src/main.cpp b/src/main.cpp
index 299b9cf9fe..469f03a911 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -89,8 +89,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
" --single-threaded source may assume it is only used single-threaded\n"
" -dynamic create a shared library (.so; .dll; .dylib)\n"
" --strip exclude debug symbols\n"
- " -target [name] -- see the targets command\n"
- " -target-glibc [version] target a specific glibc version (default: 2.17)\n"
+ " -target [name] -- see the targets command\n"
" --verbose-tokenize enable compiler debug output for tokenization\n"
" --verbose-ast enable compiler debug output for AST parsing\n"
" --verbose-link enable compiler debug output for linking\n"
@@ -128,8 +127,6 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
" --subsystem [subsystem] (windows) /SUBSYSTEM: to the linker\n"
" -F[dir] (darwin) add search path for frameworks\n"
" -framework [name] (darwin) link against framework\n"
- " -mios-version-min [ver] (darwin) set iOS deployment target\n"
- " -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target\n"
" --ver-major [ver] dynamic library semver major version\n"
" --ver-minor [ver] dynamic library semver minor version\n"
" --ver-patch [ver] dynamic library semver patch version\n"
@@ -405,7 +402,7 @@ static int main0(int argc, char **argv) {
bool link_eh_frame_hdr = false;
ErrColor color = ErrColorAuto;
CacheOpt enable_cache = CacheOptAuto;
- Buf *dynamic_linker = nullptr;
+ const char *dynamic_linker = nullptr;
const char *libc_txt = nullptr;
ZigList clang_argv = {0};
ZigList lib_dirs = {0};
@@ -416,11 +413,8 @@ static int main0(int argc, char **argv) {
bool have_libc = false;
const char *target_string = nullptr;
bool rdynamic = false;
- const char *mmacosx_version_min = nullptr;
- const char *mios_version_min = nullptr;
const char *linker_script = nullptr;
Buf *version_script = nullptr;
- const char *target_glibc = nullptr;
ZigList rpath_list = {0};
bool each_lib_rpath = false;
ZigList objects = {0};
@@ -503,7 +497,10 @@ static int main0(int argc, char **argv) {
os_path_join(get_zig_special_dir(zig_lib_dir), buf_create_from_str("build_runner.zig"), build_runner_path);
ZigTarget target;
- get_native_target(&target);
+ if ((err = target_parse_triple(&target, "native", nullptr, nullptr))) {
+ fprintf(stderr, "Unable to get native target: %s\n", err_str(err));
+ return EXIT_FAILURE;
+ }
Buf *build_file_buf = buf_create_from_str((build_file != nullptr) ? build_file : "build.zig");
Buf build_file_abs = os_path_resolve(&build_file_buf, 1);
@@ -770,7 +767,7 @@ static int main0(int argc, char **argv) {
} else if (strcmp(arg, "--name") == 0) {
out_name = argv[i];
} else if (strcmp(arg, "--dynamic-linker") == 0) {
- dynamic_linker = buf_create_from_str(argv[i]);
+ dynamic_linker = argv[i];
} else if (strcmp(arg, "--libc") == 0) {
libc_txt = argv[i];
} else if (strcmp(arg, "-D") == 0) {
@@ -844,18 +841,12 @@ static int main0(int argc, char **argv) {
cache_dir = argv[i];
} else if (strcmp(arg, "-target") == 0) {
target_string = argv[i];
- } else if (strcmp(arg, "-mmacosx-version-min") == 0) {
- mmacosx_version_min = argv[i];
- } else if (strcmp(arg, "-mios-version-min") == 0) {
- mios_version_min = argv[i];
} else if (strcmp(arg, "-framework") == 0) {
frameworks.append(argv[i]);
} else if (strcmp(arg, "--linker-script") == 0) {
linker_script = argv[i];
} else if (strcmp(arg, "--version-script") == 0) {
version_script = buf_create_from_str(argv[i]);
- } else if (strcmp(arg, "-target-glibc") == 0) {
- target_glibc = argv[i];
} else if (strcmp(arg, "-rpath") == 0) {
rpath_list.append(argv[i]);
} else if (strcmp(arg, "--test-filter") == 0) {
@@ -978,34 +969,11 @@ static int main0(int argc, char **argv) {
init_all_targets();
ZigTarget target;
- if ((err = target_parse_triple(&target, target_string, mcpu))) {
+ if ((err = target_parse_triple(&target, target_string, mcpu, dynamic_linker))) {
fprintf(stderr, "invalid target: %s\n"
"See `%s targets` to display valid targets.\n", err_str(err), arg0);
return print_error_usage(arg0);
}
- if (target_is_glibc(&target)) {
- target.glibc_version = heap::c_allocator.create();
-
- if (target_glibc != nullptr) {
- if ((err = target_parse_glibc_version(target.glibc_version, target_glibc))) {
- fprintf(stderr, "invalid glibc version '%s': %s\n", target_glibc, err_str(err));
- return print_error_usage(arg0);
- }
- } else {
- target_init_default_glibc_version(&target);
-#if defined(ZIG_OS_LINUX)
- if (target.is_native) {
- // TODO self-host glibc version detection, and then this logic can go away
- if ((err = glibc_detect_native_version(target.glibc_version))) {
- // Fall back to the default version.
- }
- }
-#endif
- }
- } else if (target_glibc != nullptr) {
- fprintf(stderr, "'%s' is not a glibc-compatible target", target_string);
- return print_error_usage(arg0);
- }
Buf zig_triple_buf = BUF_INIT;
target_triple_zig(&zig_triple_buf, &target);
@@ -1226,7 +1194,6 @@ static int main0(int argc, char **argv) {
codegen_set_strip(g, strip);
g->is_dynamic = is_dynamic;
- g->dynamic_linker_path = dynamic_linker;
g->verbose_tokenize = verbose_tokenize;
g->verbose_ast = verbose_ast;
g->verbose_link = verbose_link;
@@ -1265,18 +1232,6 @@ static int main0(int argc, char **argv) {
}
codegen_set_rdynamic(g, rdynamic);
- if (mmacosx_version_min && mios_version_min) {
- fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n");
- return main_exit(root_progress_node, EXIT_FAILURE);
- }
-
- if (mmacosx_version_min) {
- codegen_set_mmacosx_version_min(g, buf_create_from_str(mmacosx_version_min));
- }
-
- if (mios_version_min) {
- codegen_set_mios_version_min(g, buf_create_from_str(mios_version_min));
- }
if (test_filter) {
codegen_set_test_filter(g, buf_create_from_str(test_filter));
@@ -1365,7 +1320,10 @@ static int main0(int argc, char **argv) {
return main_exit(root_progress_node, EXIT_SUCCESS);
} else if (cmd == CmdTest) {
ZigTarget native;
- get_native_target(&native);
+ if ((err = target_parse_triple(&native, "native", nullptr, nullptr))) {
+ fprintf(stderr, "Unable to get native target: %s\n", err_str(err));
+ return EXIT_FAILURE;
+ }
g->enable_cache = get_cache_opt(enable_cache, output_dir == nullptr);
codegen_build_and_link(g);
diff --git a/src/os.cpp b/src/os.cpp
index 8e0bcc433b..f65a578e17 100644
--- a/src/os.cpp
+++ b/src/os.cpp
@@ -1073,8 +1073,8 @@ static Error set_file_times(OsFile file, OsTimeStamp ts) {
return ErrorNone;
#else
struct timespec times[2] = {
- { ts.sec, ts.nsec },
- { ts.sec, ts.nsec },
+ { (time_t)ts.sec, (time_t)ts.nsec },
+ { (time_t)ts.sec, (time_t)ts.nsec },
};
if (futimens(file, times) == -1) {
switch (errno) {
diff --git a/src/stage2.cpp b/src/stage2.cpp
index 736f11622e..1f6cb2d6aa 100644
--- a/src/stage2.cpp
+++ b/src/stage2.cpp
@@ -91,7 +91,109 @@ void stage2_progress_complete_one(Stage2ProgressNode *node) {}
void stage2_progress_disable_tty(Stage2Progress *progress) {}
void stage2_progress_update_node(Stage2ProgressNode *node, size_t completed_count, size_t estimated_total_items){}
-Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu) {
+static Os get_zig_os_type(ZigLLVM_OSType os_type) {
+ switch (os_type) {
+ case ZigLLVM_UnknownOS:
+ return OsFreestanding;
+ case ZigLLVM_Ananas:
+ return OsAnanas;
+ case ZigLLVM_CloudABI:
+ return OsCloudABI;
+ case ZigLLVM_DragonFly:
+ return OsDragonFly;
+ case ZigLLVM_FreeBSD:
+ return OsFreeBSD;
+ case ZigLLVM_Fuchsia:
+ return OsFuchsia;
+ case ZigLLVM_IOS:
+ return OsIOS;
+ case ZigLLVM_KFreeBSD:
+ return OsKFreeBSD;
+ case ZigLLVM_Linux:
+ return OsLinux;
+ case ZigLLVM_Lv2:
+ return OsLv2;
+ case ZigLLVM_Darwin:
+ case ZigLLVM_MacOSX:
+ return OsMacOSX;
+ case ZigLLVM_NetBSD:
+ return OsNetBSD;
+ case ZigLLVM_OpenBSD:
+ return OsOpenBSD;
+ case ZigLLVM_Solaris:
+ return OsSolaris;
+ case ZigLLVM_Win32:
+ return OsWindows;
+ case ZigLLVM_Haiku:
+ return OsHaiku;
+ case ZigLLVM_Minix:
+ return OsMinix;
+ case ZigLLVM_RTEMS:
+ return OsRTEMS;
+ case ZigLLVM_NaCl:
+ return OsNaCl;
+ case ZigLLVM_CNK:
+ return OsCNK;
+ case ZigLLVM_AIX:
+ return OsAIX;
+ case ZigLLVM_CUDA:
+ return OsCUDA;
+ case ZigLLVM_NVCL:
+ return OsNVCL;
+ case ZigLLVM_AMDHSA:
+ return OsAMDHSA;
+ case ZigLLVM_PS4:
+ return OsPS4;
+ case ZigLLVM_ELFIAMCU:
+ return OsELFIAMCU;
+ case ZigLLVM_TvOS:
+ return OsTvOS;
+ case ZigLLVM_WatchOS:
+ return OsWatchOS;
+ case ZigLLVM_Mesa3D:
+ return OsMesa3D;
+ case ZigLLVM_Contiki:
+ return OsContiki;
+ case ZigLLVM_AMDPAL:
+ return OsAMDPAL;
+ case ZigLLVM_HermitCore:
+ return OsHermitCore;
+ case ZigLLVM_Hurd:
+ return OsHurd;
+ case ZigLLVM_WASI:
+ return OsWASI;
+ case ZigLLVM_Emscripten:
+ return OsEmscripten;
+ }
+ zig_unreachable();
+}
+
+static void get_native_target(ZigTarget *target) {
+ // first zero initialize
+ *target = {};
+
+ ZigLLVM_OSType os_type;
+ ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os
+ ZigLLVMGetNativeTarget(
+ &target->arch,
+ &target->vendor,
+ &os_type,
+ &target->abi,
+ &oformat);
+ target->os = get_zig_os_type(os_type);
+ target->is_native = true;
+ if (target->abi == ZigLLVM_UnknownEnvironment) {
+ target->abi = target_default_abi(target->arch, target->os);
+ }
+ if (target_is_glibc(target)) {
+ target->glibc_or_darwin_version = heap::c_allocator.create();
+ target_init_default_glibc_version(target);
+ }
+}
+
+Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu,
+ const char *dynamic_linker)
+{
Error err;
if (zig_triple == nullptr) {
@@ -100,13 +202,11 @@ Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, cons
if (mcpu == nullptr) {
target->llvm_cpu_name = ZigLLVMGetHostCPUName();
target->llvm_cpu_features = ZigLLVMGetNativeFeatures();
- target->builtin_str = "Target.Cpu.baseline(arch);\n";
target->cache_hash = "native\n\n";
} else if (strcmp(mcpu, "baseline") == 0) {
target->is_native = false;
target->llvm_cpu_name = "";
target->llvm_cpu_features = "";
- target->builtin_str = "Target.Cpu.baseline(arch);\n";
target->cache_hash = "baseline\n\n";
} else {
const char *msg = "stage0 can't handle CPU/features in the target";
@@ -148,10 +248,12 @@ Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, cons
const char *msg = "stage0 can't handle CPU/features in the target";
stage2_panic(msg, strlen(msg));
}
- target->builtin_str = "Target.Cpu.baseline(arch);\n";
target->cache_hash = "\n\n";
}
+ if (dynamic_linker != nullptr) {
+ target->dynamic_linker = dynamic_linker;
+ }
return ErrorNone;
}
@@ -186,11 +288,6 @@ enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc) {
stage2_panic(msg, strlen(msg));
}
-enum Error stage2_detect_dynamic_linker(const struct ZigTarget *target, char **out_ptr, size_t *out_len) {
- const char *msg = "stage0 called stage2_detect_dynamic_linker";
- stage2_panic(msg, strlen(msg));
-}
-
enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths) {
native_paths->include_dirs_ptr = nullptr;
native_paths->include_dirs_len = 0;
diff --git a/src/stage2.h b/src/stage2.h
index e6db241e88..b73269f4e8 100644
--- a/src/stage2.h
+++ b/src/stage2.h
@@ -103,6 +103,8 @@ enum Error {
ErrorWindowsSdkNotFound,
ErrorUnknownDynamicLinkerPath,
ErrorTargetHasNoDynamicLinker,
+ ErrorInvalidAbiVersion,
+ ErrorInvalidOperatingSystemVersion,
};
// ABI warning
@@ -268,14 +270,12 @@ enum Os {
};
// ABI warning
-struct ZigGLibCVersion {
- uint32_t major; // always 2
+struct Stage2SemVer {
+ uint32_t major;
uint32_t minor;
uint32_t patch;
};
-struct Stage2TargetData;
-
// ABI warning
struct ZigTarget {
enum ZigLLVM_ArchType arch;
@@ -286,20 +286,20 @@ struct ZigTarget {
bool is_native;
- struct ZigGLibCVersion *glibc_version; // null means default
+ // null means default. this is double-purposed to be darwin min version
+ struct Stage2SemVer *glibc_or_darwin_version;
const char *llvm_cpu_name;
const char *llvm_cpu_features;
- const char *builtin_str;
+ const char *cpu_builtin_str;
const char *cache_hash;
+ const char *os_builtin_str;
+ const char *dynamic_linker;
};
// ABI warning
-ZIG_EXTERN_C enum Error stage2_detect_dynamic_linker(const struct ZigTarget *target,
- char **out_ptr, size_t *out_len);
-
-// ABI warning
-ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu);
+ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu,
+ const char *dynamic_linker);
// ABI warning
diff --git a/src/target.cpp b/src/target.cpp
index 161b8330d6..db1e4be809 100644
--- a/src/target.cpp
+++ b/src/target.cpp
@@ -286,83 +286,6 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type) {
zig_unreachable();
}
-static Os get_zig_os_type(ZigLLVM_OSType os_type) {
- switch (os_type) {
- case ZigLLVM_UnknownOS:
- return OsFreestanding;
- case ZigLLVM_Ananas:
- return OsAnanas;
- case ZigLLVM_CloudABI:
- return OsCloudABI;
- case ZigLLVM_DragonFly:
- return OsDragonFly;
- case ZigLLVM_FreeBSD:
- return OsFreeBSD;
- case ZigLLVM_Fuchsia:
- return OsFuchsia;
- case ZigLLVM_IOS:
- return OsIOS;
- case ZigLLVM_KFreeBSD:
- return OsKFreeBSD;
- case ZigLLVM_Linux:
- return OsLinux;
- case ZigLLVM_Lv2:
- return OsLv2;
- case ZigLLVM_Darwin:
- case ZigLLVM_MacOSX:
- return OsMacOSX;
- case ZigLLVM_NetBSD:
- return OsNetBSD;
- case ZigLLVM_OpenBSD:
- return OsOpenBSD;
- case ZigLLVM_Solaris:
- return OsSolaris;
- case ZigLLVM_Win32:
- return OsWindows;
- case ZigLLVM_Haiku:
- return OsHaiku;
- case ZigLLVM_Minix:
- return OsMinix;
- case ZigLLVM_RTEMS:
- return OsRTEMS;
- case ZigLLVM_NaCl:
- return OsNaCl;
- case ZigLLVM_CNK:
- return OsCNK;
- case ZigLLVM_AIX:
- return OsAIX;
- case ZigLLVM_CUDA:
- return OsCUDA;
- case ZigLLVM_NVCL:
- return OsNVCL;
- case ZigLLVM_AMDHSA:
- return OsAMDHSA;
- case ZigLLVM_PS4:
- return OsPS4;
- case ZigLLVM_ELFIAMCU:
- return OsELFIAMCU;
- case ZigLLVM_TvOS:
- return OsTvOS;
- case ZigLLVM_WatchOS:
- return OsWatchOS;
- case ZigLLVM_Mesa3D:
- return OsMesa3D;
- case ZigLLVM_Contiki:
- return OsContiki;
- case ZigLLVM_AMDPAL:
- return OsAMDPAL;
- case ZigLLVM_HermitCore:
- return OsHermitCore;
- case ZigLLVM_Hurd:
- return OsHurd;
- case ZigLLVM_WASI:
- return OsWASI;
- case ZigLLVM_Emscripten:
- return OsEmscripten;
- }
- zig_unreachable();
-}
-
const char *target_os_name(Os os_type) {
switch (os_type) {
case OsFreestanding:
@@ -423,7 +346,7 @@ const char *target_abi_name(ZigLLVM_EnvironmentType abi) {
return ZigLLVMGetEnvironmentTypeName(abi);
}
-Error target_parse_glibc_version(ZigGLibCVersion *glibc_ver, const char *text) {
+Error target_parse_glibc_version(Stage2SemVer *glibc_ver, const char *text) {
glibc_ver->major = 2;
glibc_ver->minor = 0;
glibc_ver->patch = 0;
@@ -446,31 +369,8 @@ Error target_parse_glibc_version(ZigGLibCVersion *glibc_ver, const char *text) {
return ErrorNone;
}
-void get_native_target(ZigTarget *target) {
- // first zero initialize
- *target = {};
-
- ZigLLVM_OSType os_type;
- ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os
- ZigLLVMGetNativeTarget(
- &target->arch,
- &target->vendor,
- &os_type,
- &target->abi,
- &oformat);
- target->os = get_zig_os_type(os_type);
- target->is_native = true;
- if (target->abi == ZigLLVM_UnknownEnvironment) {
- target->abi = target_default_abi(target->arch, target->os);
- }
- if (target_is_glibc(target)) {
- target->glibc_version = heap::c_allocator.create();
- target_init_default_glibc_version(target);
- }
-}
-
void target_init_default_glibc_version(ZigTarget *target) {
- *target->glibc_version = {2, 17, 0};
+ *target->glibc_or_darwin_version = {2, 17, 0};
}
Error target_parse_arch(ZigLLVM_ArchType *out_arch, const char *arch_ptr, size_t arch_len) {
@@ -509,8 +409,8 @@ Error target_parse_abi(ZigLLVM_EnvironmentType *out_abi, const char *abi_ptr, si
return ErrorUnknownABI;
}
-Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu) {
- return stage2_target_parse(target, triple, mcpu);
+Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu, const char *dynamic_linker) {
+ return stage2_target_parse(target, triple, mcpu, dynamic_linker);
}
const char *target_arch_name(ZigLLVM_ArchType arch) {
diff --git a/src/target.hpp b/src/target.hpp
index 9396eb2623..01f2c6b168 100644
--- a/src/target.hpp
+++ b/src/target.hpp
@@ -41,12 +41,12 @@ enum CIntType {
CIntTypeCount,
};
-Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu);
+Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu, const char *dynamic_linker);
Error target_parse_arch(ZigLLVM_ArchType *arch, const char *arch_ptr, size_t arch_len);
Error target_parse_os(Os *os, const char *os_ptr, size_t os_len);
Error target_parse_abi(ZigLLVM_EnvironmentType *abi, const char *abi_ptr, size_t abi_len);
-Error target_parse_glibc_version(ZigGLibCVersion *out, const char *text);
+Error target_parse_glibc_version(Stage2SemVer *out, const char *text);
void target_init_default_glibc_version(ZigTarget *target);
size_t target_arch_count(void);
@@ -73,7 +73,6 @@ ZigLLVM_ObjectFormatType target_oformat_enum(size_t index);
const char *target_oformat_name(ZigLLVM_ObjectFormatType oformat);
ZigLLVM_ObjectFormatType target_object_format(const ZigTarget *target);
-void get_native_target(ZigTarget *target);
void target_triple_llvm(Buf *triple, const ZigTarget *target);
void target_triple_zig(Buf *triple, const ZigTarget *target);
diff --git a/test/assemble_and_link.zig b/test/assemble_and_link.zig
index 8c727e87b5..86209bd034 100644
--- a/test/assemble_and_link.zig
+++ b/test/assemble_and_link.zig
@@ -1,8 +1,8 @@
-const builtin = @import("builtin");
+const std = @import("std");
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompareOutputContext) void {
- if (builtin.os == builtin.Os.linux and builtin.arch == builtin.Arch.x86_64) {
+ if (std.Target.current.os.tag == .linux and std.Target.current.cpu.arch == .x86_64) {
cases.addAsm("hello world linux x86_64",
\\.text
\\.globl _start
diff --git a/test/cli.zig b/test/cli.zig
index b7d03d9e21..117c714a29 100644
--- a/test/cli.zig
+++ b/test/cli.zig
@@ -1,5 +1,4 @@
const std = @import("std");
-const builtin = @import("builtin");
const testing = std.testing;
const process = std.process;
const fs = std.fs;
@@ -93,11 +92,11 @@ fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {
fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
_ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });
const run_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "run" });
- testing.expect(std.mem.eql(u8, run_result.stderr, "All your base are belong to us.\n"));
+ testing.expect(std.mem.eql(u8, run_result.stderr, "All your codebase are belong to us.\n"));
}
fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
- if (builtin.os != .linux or builtin.arch != .x86_64) return;
+ if (std.Target.current.os.tag != .linux or std.Target.current.cpu.arch != .x86_64) return;
const example_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.zig" });
const example_s_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.s" });
diff --git a/test/compare_output.zig b/test/compare_output.zig
index 7a41d46f54..ec89af35f8 100644
--- a/test/compare_output.zig
+++ b/test/compare_output.zig
@@ -1,4 +1,3 @@
-const builtin = @import("builtin");
const std = @import("std");
const os = std.os;
const tests = @import("tests.zig");
@@ -131,8 +130,8 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
, "Hello, world!\n 12 12 a\n");
cases.addC("number literals",
- \\const builtin = @import("builtin");
- \\const is_windows = builtin.os == builtin.Os.windows;
+ \\const std = @import("std");
+ \\const is_windows = std.Target.current.os.tag == .windows;
\\const c = @cImport({
\\ if (is_windows) {
\\ // See https://github.com/ziglang/zig/issues/515
@@ -306,8 +305,8 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
, "");
cases.addC("casting between float and integer types",
- \\const builtin = @import("builtin");
- \\const is_windows = builtin.os == builtin.Os.windows;
+ \\const std = @import("std");
+ \\const is_windows = std.Target.current.os.tag == .windows;
\\const c = @cImport({
\\ if (is_windows) {
\\ // See https://github.com/ziglang/zig/issues/515
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 56df006e82..979bf45bbe 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,8 +1,34 @@
const tests = @import("tests.zig");
-const builtin = @import("builtin");
-const Target = @import("std").Target;
+const std = @import("std");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.addTest("type mismatch with tuple concatenation",
+ \\export fn entry() void {
+ \\ var x = .{};
+ \\ x = x ++ .{ 1, 2, 3 };
+ \\}
+ , &[_][]const u8{
+ "tmp.zig:3:11: error: expected type 'struct:2:14', found 'struct:3:11'",
+ });
+
+ cases.addTest("@tagName on invalid value of non-exhaustive enum",
+ \\test "enum" {
+ \\ const E = enum(u8) {A, B, _};
+ \\ _ = @tagName(@intToEnum(E, 5));
+ \\}
+ , &[_][]const u8{
+ "tmp.zig:3:18: error: no tag by value 5",
+ });
+
+ cases.addTest("@ptrToInt with pointer to zero-sized type",
+ \\export fn entry() void {
+ \\ var pointer: ?*u0 = null;
+ \\ var x = @ptrToInt(pointer);
+ \\}
+ , &[_][]const u8{
+ "tmp.zig:3:23: error: pointer to size 0 type has no address",
+ });
+
cases.addTest("slice to pointer conversion mismatch",
\\pub fn bytesAsSlice(bytes: var) [*]align(1) const u16 {
\\ return @ptrCast([*]align(1) const u16, bytes.ptr)[0..1];
@@ -360,12 +386,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
, &[_][]const u8{
"tmp.zig:3:5: error: target arch 'wasm32' does not support calling with a new stack",
});
- tc.target = Target{
- .Cross = .{
- .cpu = Target.Cpu.baseline(.wasm32),
- .os = .wasi,
- .abi = .none,
- },
+ tc.target = std.zig.CrossTarget{
+ .cpu_arch = .wasm32,
+ .os_tag = .wasi,
+ .abi = .none,
};
break :x tc;
});
@@ -761,12 +785,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
, &[_][]const u8{
"tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs",
});
- tc.target = Target{
- .Cross = .{
- .cpu = Target.Cpu.baseline(.x86_64),
- .os = .linux,
- .abi = .gnu,
- },
+ tc.target = std.zig.CrossTarget{
+ .cpu_arch = .x86_64,
+ .os_tag = .linux,
+ .abi = .gnu,
};
break :x tc;
});
@@ -1426,7 +1448,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
"tmp.zig:2:18: error: invalid operands to binary expression: 'error{A}' and 'error{B}'",
});
- if (builtin.os == builtin.Os.linux) {
+ if (std.Target.current.os.tag == .linux) {
cases.addTest("implicit dependency on libc",
\\extern "c" fn exit(u8) void;
\\export fn entry() void {
@@ -2716,16 +2738,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
"tmp.zig:5:5: error: else prong required when switching on type 'anyerror'",
});
- cases.add("inferred error set with no returned error",
- \\export fn entry() void {
- \\ foo() catch unreachable;
- \\}
- \\fn foo() !void {
- \\}
- , &[_][]const u8{
- "tmp.zig:4:11: error: function with inferred error set must return at least one possible error",
- });
-
cases.add("error not handled in switch",
\\export fn entry() void {
\\ foo(452) catch |err| switch (err) {
diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig
index e4ea7f52db..e183c6979f 100644
--- a/test/runtime_safety.zig
+++ b/test/runtime_safety.zig
@@ -745,4 +745,17 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\ (await p) catch unreachable;
\\}
);
+
+ // Slicing a C pointer returns a non-allowzero slice, thus we need to emit
+ // a safety check to ensure the pointer is not null.
+ cases.addRuntimeSafety("slicing null C pointer",
+ \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
+ \\ @import("std").os.exit(126);
+ \\}
+ \\
+ \\pub fn main() void {
+ \\ var ptr: [*c]const u32 = null;
+ \\ var slice = ptr[0..3];
+ \\}
+ );
}
diff --git a/test/src/translate_c.zig b/test/src/translate_c.zig
index 21aa92537c..9a6bd0d323 100644
--- a/test/src/translate_c.zig
+++ b/test/src/translate_c.zig
@@ -7,6 +7,7 @@ const fmt = std.fmt;
const mem = std.mem;
const fs = std.fs;
const warn = std.debug.warn;
+const CrossTarget = std.zig.CrossTarget;
pub const TranslateCContext = struct {
b: *build.Builder,
@@ -19,7 +20,7 @@ pub const TranslateCContext = struct {
sources: ArrayList(SourceFile),
expected_lines: ArrayList([]const u8),
allow_warnings: bool,
- target: std.Target = .Native,
+ target: CrossTarget = CrossTarget{},
const SourceFile = struct {
filename: []const u8,
@@ -75,7 +76,7 @@ pub const TranslateCContext = struct {
pub fn addWithTarget(
self: *TranslateCContext,
name: []const u8,
- target: std.Target,
+ target: CrossTarget,
source: []const u8,
expected_lines: []const []const u8,
) void {
diff --git a/test/stack_traces.zig b/test/stack_traces.zig
index 80a5f0a602..d00f18763b 100644
--- a/test/stack_traces.zig
+++ b/test/stack_traces.zig
@@ -1,8 +1,8 @@
-const builtin = @import("builtin");
const std = @import("std");
const os = std.os;
const tests = @import("tests.zig");
+// zig fmt: off
pub fn addCases(cases: *tests.StackTracesContext) void {
const source_return =
\\const std = @import("std");
@@ -41,6 +41,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
\\ try foo();
\\}
;
+
const source_dumpCurrentStackTrace =
\\const std = @import("std");
\\
@@ -56,8 +57,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
\\}
;
- // zig fmt: off
- switch (builtin.os) {
+ switch (std.Target.current.os.tag) {
.freebsd => {
cases.addCase(
"return",
@@ -310,15 +310,15 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
source_return,
[_][]const u8{
// debug
- \\error: TheSkyIsFalling
- \\source.zig:4:5: [address] in _main.0 (test.o)
+ \\error: TheSkyIsFalling
+ \\source.zig:4:5: [address] in main (test)
\\ return error.TheSkyIsFalling;
\\ ^
\\
,
// release-safe
- \\error: TheSkyIsFalling
- \\source.zig:4:5: [address] in _main (test.o)
+ \\error: TheSkyIsFalling
+ \\source.zig:4:5: [address] in std.start.main (test)
\\ return error.TheSkyIsFalling;
\\ ^
\\
@@ -337,21 +337,21 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
source_try_return,
[_][]const u8{
// debug
- \\error: TheSkyIsFalling
- \\source.zig:4:5: [address] in _foo (test.o)
+ \\error: TheSkyIsFalling
+ \\source.zig:4:5: [address] in foo (test)
\\ return error.TheSkyIsFalling;
\\ ^
- \\source.zig:8:5: [address] in _main.0 (test.o)
+ \\source.zig:8:5: [address] in main (test)
\\ try foo();
\\ ^
\\
,
// release-safe
- \\error: TheSkyIsFalling
- \\source.zig:4:5: [address] in _main (test.o)
+ \\error: TheSkyIsFalling
+ \\source.zig:4:5: [address] in std.start.main (test)
\\ return error.TheSkyIsFalling;
\\ ^
- \\source.zig:8:5: [address] in _main (test.o)
+ \\source.zig:8:5: [address] in std.start.main (test)
\\ try foo();
\\ ^
\\
@@ -370,33 +370,33 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
source_try_try_return_return,
[_][]const u8{
// debug
- \\error: TheSkyIsFalling
- \\source.zig:12:5: [address] in _make_error (test.o)
+ \\error: TheSkyIsFalling
+ \\source.zig:12:5: [address] in make_error (test)
\\ return error.TheSkyIsFalling;
\\ ^
- \\source.zig:8:5: [address] in _bar (test.o)
+ \\source.zig:8:5: [address] in bar (test)
\\ return make_error();
\\ ^
- \\source.zig:4:5: [address] in _foo (test.o)
+ \\source.zig:4:5: [address] in foo (test)
\\ try bar();
\\ ^
- \\source.zig:16:5: [address] in _main.0 (test.o)
+ \\source.zig:16:5: [address] in main (test)
\\ try foo();
\\ ^
\\
,
// release-safe
- \\error: TheSkyIsFalling
- \\source.zig:12:5: [address] in _main (test.o)
+ \\error: TheSkyIsFalling
+ \\source.zig:12:5: [address] in std.start.main (test)
\\ return error.TheSkyIsFalling;
\\ ^
- \\source.zig:8:5: [address] in _main (test.o)
+ \\source.zig:8:5: [address] in std.start.main (test)
\\ return make_error();
\\ ^
- \\source.zig:4:5: [address] in _main (test.o)
+ \\source.zig:4:5: [address] in std.start.main (test)
\\ try bar();
\\ ^
- \\source.zig:16:5: [address] in _main (test.o)
+ \\source.zig:16:5: [address] in std.start.main (test)
\\ try foo();
\\ ^
\\
@@ -440,7 +440,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
source_try_return,
[_][]const u8{
// debug
- \\error: TheSkyIsFalling
+ \\error: TheSkyIsFalling
\\source.zig:4:5: [address] in foo (test.obj)
\\ return error.TheSkyIsFalling;
\\ ^
@@ -466,7 +466,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
source_try_try_return_return,
[_][]const u8{
// debug
- \\error: TheSkyIsFalling
+ \\error: TheSkyIsFalling
\\source.zig:12:5: [address] in make_error (test.obj)
\\ return error.TheSkyIsFalling;
\\ ^
@@ -496,5 +496,5 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
},
else => {},
}
- // zig fmt: off
}
+// zig fmt: off
diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig
index a3341c44a9..dfc4321500 100644
--- a/test/stage1/behavior.zig
+++ b/test/stage1/behavior.zig
@@ -40,6 +40,7 @@ comptime {
_ = @import("behavior/bugs/3384.zig");
_ = @import("behavior/bugs/3586.zig");
_ = @import("behavior/bugs/3742.zig");
+ _ = @import("behavior/bugs/4560.zig");
_ = @import("behavior/bugs/394.zig");
_ = @import("behavior/bugs/421.zig");
_ = @import("behavior/bugs/529.zig");
diff --git a/test/stage1/behavior/asm.zig b/test/stage1/behavior/asm.zig
index d1404c6f45..170ad3325d 100644
--- a/test/stage1/behavior/asm.zig
+++ b/test/stage1/behavior/asm.zig
@@ -1,9 +1,10 @@
const std = @import("std");
-const config = @import("builtin");
const expect = std.testing.expect;
+const is_x86_64_linux = std.Target.current.cpu.arch == .x86_64 and std.Target.current.os.tag == .linux;
+
comptime {
- if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) {
+ if (is_x86_64_linux) {
asm (
\\.globl this_is_my_alias;
\\.type this_is_my_alias, @function;
@@ -13,7 +14,7 @@ comptime {
}
test "module level assembly" {
- if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) {
+ if (is_x86_64_linux) {
expect(this_is_my_alias() == 1234);
}
}
diff --git a/test/stage1/behavior/bugs/4560.zig b/test/stage1/behavior/bugs/4560.zig
new file mode 100644
index 0000000000..6821527894
--- /dev/null
+++ b/test/stage1/behavior/bugs/4560.zig
@@ -0,0 +1,32 @@
+const std = @import("std");
+
+test "fixed" {
+ var s: S = .{
+ .a = 1,
+ .b = .{
+ .size = 123,
+ .max_distance_from_start_index = 456,
+ },
+ };
+ std.testing.expect(s.a == 1);
+ std.testing.expect(s.b.size == 123);
+ std.testing.expect(s.b.max_distance_from_start_index == 456);
+}
+
+const S = struct {
+ a: u32,
+ b: Map,
+
+ const Map = StringHashMap(*S);
+};
+
+pub fn StringHashMap(comptime V: type) type {
+ return HashMap([]const u8, V);
+}
+
+pub fn HashMap(comptime K: type, comptime V: type) type {
+ return struct {
+ size: usize,
+ max_distance_from_start_index: usize,
+ };
+}
diff --git a/test/stage1/behavior/byteswap.zig b/test/stage1/behavior/byteswap.zig
index c799ba4849..d6e07e7a56 100644
--- a/test/stage1/behavior/byteswap.zig
+++ b/test/stage1/behavior/byteswap.zig
@@ -1,6 +1,5 @@
const std = @import("std");
const expect = std.testing.expect;
-const builtin = @import("builtin");
test "@byteSwap integers" {
const ByteSwapIntTest = struct {
@@ -41,10 +40,10 @@ test "@byteSwap integers" {
test "@byteSwap vectors" {
// https://github.com/ziglang/zig/issues/3563
- if (builtin.os == .dragonfly) return error.SkipZigTest;
+ if (std.Target.current.os.tag == .dragonfly) return error.SkipZigTest;
// https://github.com/ziglang/zig/issues/3317
- if (builtin.arch == .mipsel) return error.SkipZigTest;
+ if (std.Target.current.cpu.arch == .mipsel) return error.SkipZigTest;
const ByteSwapVectorTest = struct {
fn run() void {
diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig
index 1a74eb341a..080f50cc03 100644
--- a/test/stage1/behavior/cast.zig
+++ b/test/stage1/behavior/cast.zig
@@ -487,6 +487,17 @@ test "@intToEnum passed a comptime_int to an enum with one item" {
expect(x == E.A);
}
+test "@intToEnum runtime to an extern enum with duplicate values" {
+ const E = extern enum(u8) {
+ A = 1,
+ B = 1,
+ };
+ var a: u8 = 1;
+ var x = @intToEnum(E, a);
+ expect(x == E.A);
+ expect(x == E.B);
+}
+
test "@intCast to u0 and use the result" {
const S = struct {
fn doTheTest(zero: u1, one: u1, bigzero: i32) void {
diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig
index 18489ba24b..b6cb86a363 100644
--- a/test/stage1/behavior/enum.zig
+++ b/test/stage1/behavior/enum.zig
@@ -198,7 +198,17 @@ test "@tagName" {
comptime expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three"));
}
-fn testEnumTagNameBare(n: BareNumber) []const u8 {
+test "@tagName extern enum with duplicates" {
+ expect(mem.eql(u8, testEnumTagNameBare(ExternDuplicates.B), "A"));
+ comptime expect(mem.eql(u8, testEnumTagNameBare(ExternDuplicates.B), "A"));
+}
+
+test "@tagName non-exhaustive enum" {
+ expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
+ comptime expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
+}
+
+fn testEnumTagNameBare(n: var) []const u8 {
return @tagName(n);
}
@@ -208,6 +218,17 @@ const BareNumber = enum {
Three,
};
+const ExternDuplicates = extern enum(u8) {
+ A = 1,
+ B = 1,
+};
+
+const NonExhaustive = enum(u8) {
+ A,
+ B,
+ _,
+};
+
test "enum alignment" {
comptime {
expect(@alignOf(AlignTestEnum) >= @alignOf([9]u8));
diff --git a/test/stage1/behavior/eval.zig b/test/stage1/behavior/eval.zig
index 8e70e97aaa..55ace6198c 100644
--- a/test/stage1/behavior/eval.zig
+++ b/test/stage1/behavior/eval.zig
@@ -807,3 +807,28 @@ test "return 0 from function that has u0 return type" {
}
}
}
+
+test "two comptime calls with array default initialized to undefined" {
+ const S = struct {
+ const CrossTarget = struct {
+ dynamic_linker: DynamicLinker = DynamicLinker{},
+
+ pub fn parse() void {
+ var result: CrossTarget = .{ };
+ result.getCpuArch();
+ }
+
+ pub fn getCpuArch(self: CrossTarget) void { }
+ };
+
+ const DynamicLinker = struct {
+ buffer: [255]u8 = undefined,
+ };
+
+ };
+
+ comptime {
+ S.CrossTarget.parse();
+ S.CrossTarget.parse();
+ }
+}
diff --git a/test/stage1/behavior/fn.zig b/test/stage1/behavior/fn.zig
index 13859b92ca..c1e5459378 100644
--- a/test/stage1/behavior/fn.zig
+++ b/test/stage1/behavior/fn.zig
@@ -1,4 +1,7 @@
-const expect = @import("std").testing.expect;
+const std = @import("std");
+const testing = std.testing;
+const expect = testing.expect;
+const expectEqual = testing.expectEqual;
test "params" {
expect(testParamsAdd(22, 11) == 33);
@@ -272,3 +275,12 @@ test "ability to give comptime types and non comptime types to same parameter" {
S.doTheTest();
comptime S.doTheTest();
}
+
+test "function with inferred error set but returning no error" {
+ const S = struct {
+ fn foo() !void {}
+ };
+
+ const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?;
+ expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len);
+}
diff --git a/test/stage1/behavior/math.zig b/test/stage1/behavior/math.zig
index ea92449915..a9c93850b6 100644
--- a/test/stage1/behavior/math.zig
+++ b/test/stage1/behavior/math.zig
@@ -525,7 +525,7 @@ test "comptime_int xor" {
}
test "f128" {
- if (std.Target.current.isWindows()) {
+ if (std.Target.current.os.tag == .windows) {
// TODO https://github.com/ziglang/zig/issues/508
return error.SkipZigTest;
}
@@ -619,10 +619,6 @@ test "vector integer addition" {
}
test "NaN comparison" {
- if (std.Target.current.isWindows()) {
- // TODO https://github.com/ziglang/zig/issues/508
- return error.SkipZigTest;
- }
testNanEqNan(f16);
testNanEqNan(f32);
testNanEqNan(f64);
diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig
index 681f5be500..44a8334b5d 100644
--- a/test/stage1/behavior/misc.zig
+++ b/test/stage1/behavior/misc.zig
@@ -335,7 +335,7 @@ test "string concatenation" {
comptime expect(@TypeOf(a) == *const [12:0]u8);
comptime expect(@TypeOf(b) == *const [12:0]u8);
- const len = mem.len(u8, b);
+ const len = mem.len(b);
const len_with_null = len + 1;
{
var i: u32 = 0;
diff --git a/test/stage1/behavior/namespace_depends_on_compile_var.zig b/test/stage1/behavior/namespace_depends_on_compile_var.zig
index 4c4fc4eefe..8c5c19d733 100644
--- a/test/stage1/behavior/namespace_depends_on_compile_var.zig
+++ b/test/stage1/behavior/namespace_depends_on_compile_var.zig
@@ -1,5 +1,5 @@
-const builtin = @import("builtin");
-const expect = @import("std").testing.expect;
+const std = @import("std");
+const expect = std.testing.expect;
test "namespace depends on compile var" {
if (some_namespace.a_bool) {
@@ -8,7 +8,7 @@ test "namespace depends on compile var" {
expect(!some_namespace.a_bool);
}
}
-const some_namespace = switch (builtin.os) {
- builtin.Os.linux => @import("namespace_depends_on_compile_var/a.zig"),
+const some_namespace = switch (std.builtin.os.tag) {
+ .linux => @import("namespace_depends_on_compile_var/a.zig"),
else => @import("namespace_depends_on_compile_var/b.zig"),
};
diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig
index fdaa5867d7..bcc1d62df3 100644
--- a/test/stage1/behavior/pointers.zig
+++ b/test/stage1/behavior/pointers.zig
@@ -318,3 +318,15 @@ test "pointer arithmetic affects the alignment" {
expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
}
}
+
+test "@ptrToInt on null optional at comptime" {
+ {
+ const pointer = @intToPtr(?*u8, 0x000);
+ const x = @ptrToInt(pointer);
+ comptime expect(0 == @ptrToInt(pointer));
+ }
+ {
+ const pointer = @intToPtr(?*u8, 0xf00);
+ comptime expect(0xf00 == @ptrToInt(pointer));
+ }
+}
diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig
index d1e1fed40f..a729bf731d 100644
--- a/test/stage1/behavior/sizeof_and_typeof.zig
+++ b/test/stage1/behavior/sizeof_and_typeof.zig
@@ -1,5 +1,7 @@
-const builtin = @import("builtin");
-const expect = @import("std").testing.expect;
+const std = @import("std");
+const builtin = std.builtin;
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
test "@sizeOf and @TypeOf" {
const y: @TypeOf(x) = 120;
@@ -135,3 +137,53 @@ test "@bitSizeOf" {
a: u2,
}) == 2);
}
+
+test "@sizeOf comparison against zero" {
+ const S0 = struct {
+ f: *@This(),
+ };
+ const U0 = union {
+ f: *@This(),
+ };
+ const S1 = struct {
+ fn H(comptime T: type) type {
+ return struct {
+ x: T,
+ };
+ }
+ f0: H(*@This()),
+ f1: H(**@This()),
+ f2: H(***@This()),
+ };
+ const U1 = union {
+ fn H(comptime T: type) type {
+ return struct {
+ x: T,
+ };
+ }
+ f0: H(*@This()),
+ f1: H(**@This()),
+ f2: H(***@This()),
+ };
+ const S = struct {
+ fn doTheTest(comptime T: type, comptime result: bool) void {
+ expectEqual(result, @sizeOf(T) > 0);
+ }
+ };
+ // Zero-sized type
+ S.doTheTest(u0, false);
+ S.doTheTest(*u0, false);
+ // Non byte-sized type
+ S.doTheTest(u1, true);
+ S.doTheTest(*u1, true);
+ // Regular type
+ S.doTheTest(u8, true);
+ S.doTheTest(*u8, true);
+ S.doTheTest(f32, true);
+ S.doTheTest(*f32, true);
+ // Container with ptr pointing to themselves
+ S.doTheTest(S0, true);
+ S.doTheTest(U0, true);
+ S.doTheTest(S1, true);
+ S.doTheTest(U1, true);
+}
diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig
index b985ec1f8e..9dd57f474e 100644
--- a/test/stage1/behavior/slice.zig
+++ b/test/stage1/behavior/slice.zig
@@ -1,6 +1,7 @@
const std = @import("std");
const expect = std.testing.expect;
const expectEqualSlices = std.testing.expectEqualSlices;
+const expectEqual = std.testing.expectEqual;
const mem = std.mem;
const x = @intToPtr([*]i32, 0x1000)[0..0x500];
@@ -42,6 +43,17 @@ test "C pointer" {
expectEqualSlices(u8, "kjdhfkjdhf", slice);
}
+test "C pointer slice access" {
+ var buf: [10]u32 = [1]u32{42} ** 10;
+ const c_ptr = @ptrCast([*c]const u32, &buf);
+
+ comptime expectEqual([]const u32, @TypeOf(c_ptr[0..1]));
+
+ for (c_ptr[0..5]) |*cl| {
+ expectEqual(@as(u32, 42), cl.*);
+ }
+}
+
fn sliceSum(comptime q: []const u8) i32 {
comptime var result = 0;
inline for (q) |item| {
@@ -97,3 +109,20 @@ test "obtaining a null terminated slice" {
comptime expect(@TypeOf(ptr2) == [:0]u8);
comptime expect(@TypeOf(ptr2[0..2]) == []u8);
}
+
+test "empty array to slice" {
+ const S = struct {
+ fn doTheTest() void {
+ const empty: []align(16) u8 = &[_]u8{};
+ const align_1: []align(1) u8 = empty;
+ const align_4: []align(4) u8 = empty;
+ const align_16: []align(16) u8 = empty;
+ expectEqual(1, @typeInfo(@TypeOf(align_1)).Pointer.alignment);
+ expectEqual(4, @typeInfo(@TypeOf(align_4)).Pointer.alignment);
+ expectEqual(16, @typeInfo(@TypeOf(align_16)).Pointer.alignment);
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig
index e89399c5e2..01e5ac1fb8 100644
--- a/test/stage1/behavior/vector.zig
+++ b/test/stage1/behavior/vector.zig
@@ -2,7 +2,6 @@ const std = @import("std");
const mem = std.mem;
const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
-const builtin = @import("builtin");
test "implicit cast vector to array - bool" {
const S = struct {
@@ -114,7 +113,7 @@ test "array to vector" {
test "vector casts of sizes not divisable by 8" {
// https://github.com/ziglang/zig/issues/3563
- if (builtin.os == .dragonfly) return error.SkipZigTest;
+ if (std.Target.current.os.tag == .dragonfly) return error.SkipZigTest;
const S = struct {
fn doTheTest() void {
diff --git a/test/standalone.zig b/test/standalone.zig
index 2c5b9c790e..aec9c82726 100644
--- a/test/standalone.zig
+++ b/test/standalone.zig
@@ -18,10 +18,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
cases.addBuildFile("test/standalone/use_alias/build.zig");
cases.addBuildFile("test/standalone/brace_expansion/build.zig");
cases.addBuildFile("test/standalone/empty_env/build.zig");
- if (std.Target.current.getOs() != .wasi) {
+ if (std.Target.current.os.tag != .wasi) {
cases.addBuildFile("test/standalone/load_dynamic_library/build.zig");
}
- if (std.Target.current.getArch() == .x86_64) { // TODO add C ABI support for other architectures
+ if (std.Target.current.cpu.arch == .x86_64) { // TODO add C ABI support for other architectures
cases.addBuildFile("test/stage1/c_abi/build.zig");
}
}
diff --git a/test/standalone/global_linkage/build.zig b/test/standalone/global_linkage/build.zig
new file mode 100644
index 0000000000..c6c45c008a
--- /dev/null
+++ b/test/standalone/global_linkage/build.zig
@@ -0,0 +1,23 @@
+const Builder = @import("std").build.Builder;
+
+pub fn build(b: *Builder) void {
+ const mode = b.standardReleaseOptions();
+ const target = b.standardTargetOptions(null);
+
+ const obj1 = b.addStaticLibrary("obj1", "obj1.zig");
+ obj1.setBuildMode(mode);
+ obj1.setTheTarget(target);
+
+ const obj2 = b.addStaticLibrary("obj2", "obj2.zig");
+ obj2.setBuildMode(mode);
+ obj2.setTheTarget(target);
+
+ const main = b.addTest("main.zig");
+ main.setBuildMode(mode);
+ main.setTheTarget(target);
+ main.linkLibrary(obj1);
+ main.linkLibrary(obj2);
+
+ const test_step = b.step("test", "Test it");
+ test_step.dependOn(&main.step);
+}
diff --git a/test/standalone/global_linkage/main.zig b/test/standalone/global_linkage/main.zig
new file mode 100644
index 0000000000..53d953765b
--- /dev/null
+++ b/test/standalone/global_linkage/main.zig
@@ -0,0 +1,9 @@
+const std = @import("std");
+
+extern var obj1_integer: usize;
+extern var obj2_integer: usize;
+
+test "access the external integers" {
+ std.testing.expect(obj1_integer == 421);
+ std.testing.expect(obj2_integer == 422);
+}
diff --git a/test/standalone/global_linkage/obj1.zig b/test/standalone/global_linkage/obj1.zig
new file mode 100644
index 0000000000..95814cda3d
--- /dev/null
+++ b/test/standalone/global_linkage/obj1.zig
@@ -0,0 +1,7 @@
+extern var internal_integer: usize = 1;
+extern var obj1_integer: usize = 421;
+
+comptime {
+ @export(internal_integer, .{ .name = "internal_integer", .linkage = .Internal });
+ @export(obj1_integer, .{ .name = "obj1_integer", .linkage = .Strong });
+}
diff --git a/test/standalone/global_linkage/obj2.zig b/test/standalone/global_linkage/obj2.zig
new file mode 100644
index 0000000000..f2d44b2dc0
--- /dev/null
+++ b/test/standalone/global_linkage/obj2.zig
@@ -0,0 +1,7 @@
+extern var internal_integer: usize = 2;
+extern var obj2_integer: usize = 422;
+
+comptime {
+ @export(internal_integer, .{ .name = "internal_integer", .linkage = .Internal });
+ @export(obj2_integer, .{ .name = "obj2_integer", .linkage = .Strong });
+}
diff --git a/test/tests.zig b/test/tests.zig
index 5982c69d6d..0a7f6942ba 100644
--- a/test/tests.zig
+++ b/test/tests.zig
@@ -1,16 +1,15 @@
const std = @import("std");
+const builtin = std.builtin;
const debug = std.debug;
const warn = debug.warn;
const build = std.build;
-pub const Target = build.Target;
-pub const CrossTarget = build.CrossTarget;
+const CrossTarget = std.zig.CrossTarget;
const Buffer = std.Buffer;
const io = std.io;
const fs = std.fs;
const mem = std.mem;
const fmt = std.fmt;
const ArrayList = std.ArrayList;
-const builtin = @import("builtin");
const Mode = builtin.Mode;
const LibExeObjStep = build.LibExeObjStep;
@@ -31,7 +30,7 @@ pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTransla
pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext;
const TestTarget = struct {
- target: Target = .Native,
+ target: CrossTarget = @as(CrossTarget, .{}),
mode: builtin.Mode = .Debug,
link_libc: bool = false,
single_threaded: bool = false,
@@ -53,93 +52,77 @@ const test_targets = blk: {
},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.x86_64),
- .os = .linux,
- .abi = .none,
- },
+ .target = .{
+ .cpu_arch = .x86_64,
+ .os_tag = .linux,
+ .abi = .none,
},
},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.x86_64),
- .os = .linux,
- .abi = .gnu,
- },
+ .target = .{
+ .cpu_arch = .x86_64,
+ .os_tag = .linux,
+ .abi = .gnu,
},
.link_libc = true,
},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.x86_64),
- .os = .linux,
- .abi = .musl,
- },
+ .target = .{
+ .cpu_arch = .x86_64,
+ .os_tag = .linux,
+ .abi = .musl,
},
.link_libc = true,
},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.i386),
- .os = .linux,
- .abi = .none,
- },
+ .target = .{
+ .cpu_arch = .i386,
+ .os_tag = .linux,
+ .abi = .none,
},
},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.i386),
- .os = .linux,
- .abi = .musl,
- },
+ .target = .{
+ .cpu_arch = .i386,
+ .os_tag = .linux,
+ .abi = .musl,
},
.link_libc = true,
},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.aarch64),
- .os = .linux,
- .abi = .none,
- },
+ .target = .{
+ .cpu_arch = .aarch64,
+ .os_tag = .linux,
+ .abi = .none,
},
},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.aarch64),
- .os = .linux,
- .abi = .musl,
- },
+ .target = .{
+ .cpu_arch = .aarch64,
+ .os_tag = .linux,
+ .abi = .musl,
},
.link_libc = true,
},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.aarch64),
- .os = .linux,
- .abi = .gnu,
- },
+ .target = .{
+ .cpu_arch = .aarch64,
+ .os_tag = .linux,
+ .abi = .gnu,
},
.link_libc = true,
},
TestTarget{
- .target = Target.parse(.{
+ .target = CrossTarget.parse(.{
.arch_os_abi = "arm-linux-none",
.cpu_features = "generic+v8a",
}) catch unreachable,
},
TestTarget{
- .target = Target.parse(.{
+ .target = CrossTarget.parse(.{
.arch_os_abi = "arm-linux-musleabihf",
.cpu_features = "generic+v8a",
}) catch unreachable,
@@ -147,7 +130,7 @@ const test_targets = blk: {
},
// TODO https://github.com/ziglang/zig/issues/3287
//TestTarget{
- // .target = Target.parse(.{
+ // .target = CrossTarget.parse(.{
// .arch_os_abi = "arm-linux-gnueabihf",
// .cpu_features = "generic+v8a",
// }) catch unreachable,
@@ -155,109 +138,89 @@ const test_targets = blk: {
//},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.mipsel),
- .os = .linux,
- .abi = .none,
- },
+ .target = .{
+ .cpu_arch = .mipsel,
+ .os_tag = .linux,
+ .abi = .none,
},
},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.mipsel),
- .os = .linux,
- .abi = .musl,
- },
+ .target = .{
+ .cpu_arch = .mipsel,
+ .os_tag = .linux,
+ .abi = .musl,
},
.link_libc = true,
},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.riscv64),
- .os = .linux,
- .abi = .none,
- },
+ .target = .{
+ .cpu_arch = .riscv64,
+ .os_tag = .linux,
+ .abi = .none,
},
},
// https://github.com/ziglang/zig/issues/4485
//TestTarget{
- // .target = Target{
- // .Cross = CrossTarget{
- // .cpu = Target.Cpu.baseline(.riscv64),
- // .os = .linux,
- // .abi = .musl,
- // },
+ // .target = .{
+ // .cpu_arch = .riscv64,
+ // .os_tag = .linux,
+ // .abi = .musl,
// },
// .link_libc = true,
//},
// https://github.com/ziglang/zig/issues/3340
//TestTarget{
- // .target = Target{
- // .Cross = CrossTarget{
- // .cpu = Target.Cpu.baseline(.riscv64),
- // .os = .linux,
- // .abi = .gnu,
- // },
+ // .target = .{
+ // .cpu_arch = .riscv64,
+ // .os = .linux,
+ // .abi = .gnu,
// },
// .link_libc = true,
//},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.x86_64),
- .os = .macosx,
- .abi = .gnu,
- },
+ .target = .{
+ .cpu_arch = .x86_64,
+ .os_tag = .macosx,
+ .abi = .gnu,
},
// TODO https://github.com/ziglang/zig/issues/3295
.disable_native = true,
},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.i386),
- .os = .windows,
- .abi = .msvc,
- },
+ .target = .{
+ .cpu_arch = .i386,
+ .os_tag = .windows,
+ .abi = .msvc,
},
},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.x86_64),
- .os = .windows,
- .abi = .msvc,
- },
+ .target = .{
+ .cpu_arch = .x86_64,
+ .os_tag = .windows,
+ .abi = .msvc,
},
},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.i386),
- .os = .windows,
- .abi = .gnu,
- },
+ .target = .{
+ .cpu_arch = .i386,
+ .os_tag = .windows,
+ .abi = .gnu,
},
.link_libc = true,
},
TestTarget{
- .target = Target{
- .Cross = CrossTarget{
- .cpu = Target.Cpu.baseline(.x86_64),
- .os = .windows,
- .abi = .gnu,
- },
+ .target = .{
+ .cpu_arch = .x86_64,
+ .os_tag = .windows,
+ .abi = .gnu,
},
.link_libc = true,
},
@@ -466,13 +429,13 @@ pub fn addPkgTests(
const step = b.step(b.fmt("test-{}", .{name}), desc);
for (test_targets) |test_target| {
- if (skip_non_native and test_target.target != .Native)
+ if (skip_non_native and !test_target.target.isNative())
continue;
if (skip_libc and test_target.link_libc)
continue;
- if (test_target.link_libc and test_target.target.osRequiresLibC()) {
+ if (test_target.link_libc and test_target.target.getOs().requiresLibC()) {
// This would be a redundant test.
continue;
}
@@ -482,8 +445,8 @@ pub fn addPkgTests(
const ArchTag = @TagType(builtin.Arch);
if (test_target.disable_native and
- test_target.target.getOs() == builtin.os and
- test_target.target.getArch() == builtin.arch)
+ test_target.target.getOsTag() == std.Target.current.os.tag and
+ test_target.target.getCpuArch() == std.Target.current.cpu.arch)
{
continue;
}
@@ -493,17 +456,14 @@ pub fn addPkgTests(
} else false;
if (!want_this_mode) continue;
- const libc_prefix = if (test_target.target.osRequiresLibC())
+ const libc_prefix = if (test_target.target.getOs().requiresLibC())
""
else if (test_target.link_libc)
"c"
else
"bare";
- const triple_prefix = if (test_target.target == .Native)
- @as([]const u8, "native")
- else
- test_target.target.zigTripleNoSubArch(b.allocator) catch unreachable;
+ const triple_prefix = test_target.target.zigTriple(b.allocator) catch unreachable;
const these_tests = b.addTest(root_src);
const single_threaded_txt = if (test_target.single_threaded) "single" else "multi";
@@ -517,7 +477,7 @@ pub fn addPkgTests(
these_tests.single_threaded = test_target.single_threaded;
these_tests.setFilter(test_filter);
these_tests.setBuildMode(test_target.mode);
- these_tests.setTheTarget(test_target.target);
+ these_tests.setTarget(test_target.target);
if (test_target.link_libc) {
these_tests.linkSystemLibrary("c");
}
@@ -694,7 +654,7 @@ pub const StackTracesContext = struct {
const delims = [_][]const u8{ ":", ":", ":", " in " };
var marks = [_]usize{0} ** 4;
// offset search past `[drive]:` on windows
- var pos: usize = if (builtin.os == .windows) 2 else 0;
+ var pos: usize = if (std.Target.current.os.tag == .windows) 2 else 0;
for (delims) |delim, i| {
marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse {
try buf.append(line);
@@ -747,7 +707,7 @@ pub const CompileErrorContext = struct {
link_libc: bool,
is_exe: bool,
is_test: bool,
- target: Target = .Native,
+ target: CrossTarget = CrossTarget{},
const SourceFile = struct {
filename: []const u8,
@@ -839,12 +799,9 @@ pub const CompileErrorContext = struct {
zig_args.append("--output-dir") catch unreachable;
zig_args.append(b.pathFromRoot(b.cache_root)) catch unreachable;
- switch (self.case.target) {
- .Native => {},
- .Cross => {
- try zig_args.append("-target");
- try zig_args.append(try self.case.target.zigTriple(b.allocator));
- },
+ if (!self.case.target.isNative()) {
+ try zig_args.append("-target");
+ try zig_args.append(try self.case.target.zigTriple(b.allocator));
}
switch (self.build_mode) {
diff --git a/test/translate_c.zig b/test/translate_c.zig
index 701513153c..1d6c2a60ae 100644
--- a/test/translate_c.zig
+++ b/test/translate_c.zig
@@ -1,6 +1,6 @@
const tests = @import("tests.zig");
-const builtin = @import("builtin");
-const Target = @import("std").Target;
+const std = @import("std");
+const CrossTarget = std.zig.CrossTarget;
pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("macro line continuation",
@@ -665,7 +665,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
- if (builtin.os != builtin.Os.windows) {
+ if (std.Target.current.os.tag != .windows) {
// Windows treats this as an enum with type c_int
cases.add("big negative enum init values when C ABI supports long long enums",
\\enum EnumWithInits {
@@ -1064,7 +1064,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
- if (builtin.os != builtin.Os.windows) {
+ if (std.Target.current.os.tag != .windows) {
// sysv_abi not currently supported on windows
cases.add("Macro qualified functions",
\\void __attribute__((sysv_abi)) foo(void);
@@ -1093,12 +1093,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const fn1 = ?fn (u8) callconv(.C) void;
});
- cases.addWithTarget("Calling convention", tests.Target{
- .Cross = .{
- .cpu = Target.Cpu.baseline(.i386),
- .os = .linux,
- .abi = .none,
- },
+ cases.addWithTarget("Calling convention", .{
+ .cpu_arch = .i386,
+ .os_tag = .linux,
+ .abi = .none,
},
\\void __attribute__((fastcall)) foo1(float *a);
\\void __attribute__((stdcall)) foo2(float *a);
@@ -1113,7 +1111,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub fn foo5(a: [*c]f32) callconv(.Thiscall) void;
});
- cases.addWithTarget("Calling convention", Target.parse(.{
+ cases.addWithTarget("Calling convention", CrossTarget.parse(.{
.arch_os_abi = "arm-linux-none",
.cpu_features = "generic+v8_5a",
}) catch unreachable,
@@ -1124,7 +1122,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub fn foo2(a: [*c]f32) callconv(.AAPCSVFP) void;
});
- cases.addWithTarget("Calling convention", Target.parse(.{
+ cases.addWithTarget("Calling convention", CrossTarget.parse(.{
.arch_os_abi = "aarch64-linux-none",
.cpu_features = "generic+v8_5a",
}) catch unreachable,
@@ -1596,7 +1594,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
- if (builtin.os != .windows) {
+ if (std.Target.current.os.tag != .windows) {
// When clang uses the -windows-none triple it behaves as MSVC and
// interprets the inner `struct Bar` as an anonymous structure
cases.add("type referenced struct",