aboutsummaryrefslogtreecommitdiff
path: root/std
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2019-02-07 12:21:20 -0500
committerAndrew Kelley <andrew@ziglang.org>2019-02-07 12:21:20 -0500
commita94304d3e4b9220d2b22501eb2666e5aa89df236 (patch)
treea3b85b53581853b62dabf6f763feca099e4fee84 /std
parentd974afde1d366a28f1b07bff6ebfb5c5756d3b61 (diff)
parent2b2bf53a49616192e2b2bdf40b88400964ff1500 (diff)
downloadzig-a94304d3e4b9220d2b22501eb2666e5aa89df236.tar.gz
zig-a94304d3e4b9220d2b22501eb2666e5aa89df236.zip
Merge remote-tracking branch 'origin/master' into llvm8
Diffstat (limited to 'std')
-rw-r--r--std/build.zig87
-rw-r--r--std/debug/index.zig5
-rw-r--r--std/event/fs.zig4
-rw-r--r--std/heap.zig7
-rw-r--r--std/index.zig2
-rw-r--r--std/io.zig10
-rw-r--r--std/io_test.zig11
-rw-r--r--std/mem.zig72
-rw-r--r--std/os/child_process.zig9
-rw-r--r--std/os/get_app_data_dir.zig7
-rw-r--r--std/os/index.zig113
-rw-r--r--std/os/path.zig125
-rw-r--r--std/os/test.zig16
-rw-r--r--std/os/windows/index.zig19
-rw-r--r--std/os/windows/kernel32.zig4
-rw-r--r--std/os/windows/tls.zig36
-rw-r--r--std/special/bootstrap.zig63
17 files changed, 437 insertions, 153 deletions
diff --git a/std/build.zig b/std/build.zig
index 5246d97339..0dbbded802 100644
--- a/std/build.zig
+++ b/std/build.zig
@@ -145,8 +145,8 @@ pub const Builder = struct {
pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void {
self.prefix = maybe_prefix orelse "/usr/local"; // TODO better default
- self.lib_dir = os.path.join(self.allocator, self.prefix, "lib") catch unreachable;
- self.exe_dir = os.path.join(self.allocator, self.prefix, "bin") catch unreachable;
+ self.lib_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "lib" }) catch unreachable;
+ self.exe_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "bin" }) catch unreachable;
}
pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
@@ -618,7 +618,10 @@ pub const Builder = struct {
///::dest_rel_path is relative to prefix path or it can be an absolute path
pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
- const full_dest_path = os.path.resolve(self.allocator, self.prefix, dest_rel_path) catch unreachable;
+ const full_dest_path = os.path.resolve(
+ self.allocator,
+ [][]const u8{ self.prefix, dest_rel_path },
+ ) catch unreachable;
self.pushInstalledFile(full_dest_path);
const install_step = self.allocator.create(InstallFileStep) catch unreachable;
@@ -653,7 +656,7 @@ pub const Builder = struct {
}
fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 {
- return os.path.resolve(self.allocator, self.build_root, rel_path) catch unreachable;
+ return os.path.resolve(self.allocator, [][]const u8{ self.build_root, rel_path }) catch unreachable;
}
pub fn fmt(self: *Builder, comptime format: []const u8, args: ...) []u8 {
@@ -676,7 +679,7 @@ pub const Builder = struct {
if (os.path.isAbsolute(name)) {
return name;
}
- const full_path = try os.path.join(self.allocator, search_prefix, "bin", self.fmt("{}{}", name, exe_extension));
+ const full_path = try os.path.join(self.allocator, [][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) });
if (os.path.real(self.allocator, full_path)) |real_path| {
return real_path;
} else |_| {
@@ -691,7 +694,7 @@ pub const Builder = struct {
}
var it = mem.tokenize(PATH, []u8{os.path.delimiter});
while (it.next()) |path| {
- const full_path = try os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension));
+ const full_path = try os.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) });
if (os.path.real(self.allocator, full_path)) |real_path| {
return real_path;
} else |_| {
@@ -705,7 +708,7 @@ pub const Builder = struct {
return name;
}
for (paths) |path| {
- const full_path = try os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension));
+ const full_path = try os.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) });
if (os.path.real(self.allocator, full_path)) |real_path| {
return real_path;
} else |_| {
@@ -1113,7 +1116,10 @@ pub const LibExeObjStep = struct {
}
pub fn getOutputPath(self: *LibExeObjStep) []const u8 {
- return if (self.output_path) |output_path| output_path else os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename) catch unreachable;
+ return if (self.output_path) |output_path| output_path else os.path.join(
+ self.builder.allocator,
+ [][]const u8{ self.builder.cache_root, self.out_filename },
+ ) catch unreachable;
}
pub fn setOutputHPath(self: *LibExeObjStep, file_path: []const u8) void {
@@ -1126,7 +1132,10 @@ pub const LibExeObjStep = struct {
}
pub fn getOutputHPath(self: *LibExeObjStep) []const u8 {
- return if (self.output_h_path) |output_h_path| output_h_path else os.path.join(self.builder.allocator, self.builder.cache_root, self.out_h_filename) catch unreachable;
+ return if (self.output_h_path) |output_h_path| output_h_path else os.path.join(
+ self.builder.allocator,
+ [][]const u8{ self.builder.cache_root, self.out_h_filename },
+ ) catch unreachable;
}
pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
@@ -1226,7 +1235,10 @@ pub const LibExeObjStep = struct {
}
if (self.build_options_contents.len() > 0) {
- const build_options_file = try os.path.join(builder.allocator, builder.cache_root, builder.fmt("{}_build_options.zig", self.name));
+ const build_options_file = try os.path.join(
+ builder.allocator,
+ [][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", self.name) },
+ );
try std.io.writeFile(build_options_file, self.build_options_contents.toSliceConst());
try zig_args.append("--pkg-begin");
try zig_args.append("build_options");
@@ -1476,7 +1488,10 @@ pub const LibExeObjStep = struct {
cc_args.append("-c") catch unreachable;
cc_args.append(abs_source_file) catch unreachable;
- const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable;
+ const cache_o_src = os.path.join(
+ builder.allocator,
+ [][]const u8{ builder.cache_root, source_file },
+ ) catch unreachable;
if (os.path.dirname(cache_o_src)) |cache_o_dir| {
try builder.makePath(cache_o_dir);
}
@@ -1528,7 +1543,10 @@ pub const LibExeObjStep = struct {
cc_args.append("-current_version") catch unreachable;
cc_args.append(builder.fmt("{}.{}.{}", self.version.major, self.version.minor, self.version.patch)) catch unreachable;
- const install_name = builder.pathFromRoot(os.path.join(builder.allocator, builder.cache_root, self.major_only_filename) catch unreachable);
+ const install_name = builder.pathFromRoot(os.path.join(
+ builder.allocator,
+ [][]const u8{ builder.cache_root, self.major_only_filename },
+ ) catch unreachable);
cc_args.append("-install_name") catch unreachable;
cc_args.append(install_name) catch unreachable;
} else {
@@ -1594,7 +1612,10 @@ pub const LibExeObjStep = struct {
cc_args.append("-c") catch unreachable;
cc_args.append(abs_source_file) catch unreachable;
- const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable;
+ const cache_o_src = os.path.join(
+ builder.allocator,
+ [][]const u8{ builder.cache_root, source_file },
+ ) catch unreachable;
if (os.path.dirname(cache_o_src)) |cache_o_dir| {
try builder.makePath(cache_o_dir);
}
@@ -1686,6 +1707,7 @@ pub const TestStep = struct {
no_rosegment: bool,
output_path: ?[]const u8,
system_linker_hack: bool,
+ override_std_dir: ?[]const u8,
pub fn init(builder: *Builder, root_src: []const u8) TestStep {
const step_name = builder.fmt("test {}", root_src);
@@ -1707,6 +1729,7 @@ pub const TestStep = struct {
.no_rosegment = false,
.output_path = null,
.system_linker_hack = false,
+ .override_std_dir = null,
};
}
@@ -1737,6 +1760,10 @@ pub const TestStep = struct {
self.build_mode = mode;
}
+ pub fn overrideStdDir(self: *TestStep, dir_path: []const u8) void {
+ self.override_std_dir = dir_path;
+ }
+
pub fn setOutputPath(self: *TestStep, file_path: []const u8) void {
self.output_path = file_path;
@@ -1751,7 +1778,10 @@ pub const TestStep = struct {
return output_path;
} else {
const basename = self.builder.fmt("test{}", self.target.exeFileExt());
- return os.path.join(self.builder.allocator, self.builder.cache_root, basename) catch unreachable;
+ return os.path.join(
+ self.builder.allocator,
+ [][]const u8{ self.builder.cache_root, basename },
+ ) catch unreachable;
}
}
@@ -1914,6 +1944,10 @@ pub const TestStep = struct {
if (self.system_linker_hack) {
try zig_args.append("--system-linker-hack");
}
+ if (self.override_std_dir) |dir| {
+ try zig_args.append("--override-std-dir");
+ try zig_args.append(builder.pathFromRoot(dir));
+ }
try builder.spawnChild(zig_args.toSliceConst());
}
@@ -1969,13 +2003,22 @@ const InstallArtifactStep = struct {
.builder = builder,
.step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make),
.artifact = artifact,
- .dest_file = os.path.join(builder.allocator, dest_dir, artifact.out_filename) catch unreachable,
+ .dest_file = os.path.join(
+ builder.allocator,
+ [][]const u8{ dest_dir, artifact.out_filename },
+ ) catch unreachable,
};
self.step.dependOn(&artifact.step);
builder.pushInstalledFile(self.dest_file);
if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) {
- builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, artifact.major_only_filename) catch unreachable);
- builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, artifact.name_only_filename) catch unreachable);
+ builder.pushInstalledFile(os.path.join(
+ builder.allocator,
+ [][]const u8{ builder.lib_dir, artifact.major_only_filename },
+ ) catch unreachable);
+ builder.pushInstalledFile(os.path.join(
+ builder.allocator,
+ [][]const u8{ builder.lib_dir, artifact.name_only_filename },
+ ) catch unreachable);
}
return self;
}
@@ -2131,13 +2174,19 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj
const out_dir = os.path.dirname(output_path) orelse ".";
const out_basename = os.path.basename(output_path);
// sym link for libfoo.so.1 to libfoo.so.1.2.3
- const major_only_path = os.path.join(allocator, out_dir, filename_major_only) catch unreachable;
+ const major_only_path = os.path.join(
+ allocator,
+ [][]const u8{ out_dir, filename_major_only },
+ ) catch unreachable;
os.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
warn("Unable to symlink {} -> {}\n", major_only_path, out_basename);
return err;
};
// sym link for libfoo.so to libfoo.so.1
- const name_only_path = os.path.join(allocator, out_dir, filename_name_only) catch unreachable;
+ const name_only_path = os.path.join(
+ allocator,
+ [][]const u8{ out_dir, filename_name_only },
+ ) catch unreachable;
os.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
warn("Unable to symlink {} -> {}\n", name_only_path, filename_major_only);
return err;
diff --git a/std/debug/index.zig b/std/debug/index.zig
index 838bd0c166..a1e2747df5 100644
--- a/std/debug/index.zig
+++ b/std/debug/index.zig
@@ -37,7 +37,6 @@ const Module = struct {
var stderr_file: os.File = undefined;
var stderr_file_out_stream: os.File.OutStream = undefined;
-/// TODO multithreaded awareness
var stderr_stream: ?*io.OutStream(os.File.WriteError) = null;
var stderr_mutex = std.Mutex.init();
pub fn warn(comptime fmt: []const u8, args: ...) void {
@@ -775,7 +774,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
const len = try di.coff.getPdbPath(path_buf[0..]);
const raw_path = path_buf[0..len];
- const path = try os.path.resolve(allocator, raw_path);
+ const path = try os.path.resolve(allocator, [][]const u8{raw_path});
try di.pdb.openFile(di.coff, path);
@@ -1353,7 +1352,7 @@ const LineNumberProgram = struct {
return error.InvalidDebugInfo;
} else
self.include_dirs[file_entry.dir_index];
- const file_name = try os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name);
+ const file_name = try os.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(usize, self.prev_line) else 0,
diff --git a/std/event/fs.zig b/std/event/fs.zig
index 097f2beddc..fd0fe434cb 100644
--- a/std/event/fs.zig
+++ b/std/event/fs.zig
@@ -871,7 +871,7 @@ pub fn Watch(comptime V: type) type {
}
async fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V {
- const resolved_path = try os.path.resolve(self.channel.loop.allocator, file_path);
+ const resolved_path = try os.path.resolve(self.channel.loop.allocator, [][]const u8{file_path});
var resolved_path_consumed = false;
defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path);
@@ -1336,7 +1336,7 @@ async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void {
}
async fn testFsWatch(loop: *Loop) !void {
- const file_path = try os.path.join(loop.allocator, test_tmp_dir, "file.txt");
+ const file_path = try os.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" });
defer loop.allocator.free(file_path);
const contents =
diff --git a/std/heap.zig b/std/heap.zig
index fd2ce1e965..1403f8e831 100644
--- a/std/heap.zig
+++ b/std/heap.zig
@@ -106,9 +106,7 @@ pub const DirectAllocator = struct {
};
const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
const root_addr = @ptrToInt(ptr);
- const rem = @rem(root_addr, alignment);
- const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
- const adjusted_addr = root_addr + march_forward_bytes;
+ const adjusted_addr = mem.alignForward(root_addr, alignment);
const record_addr = adjusted_addr + n;
@intToPtr(*align(1) usize, record_addr).* = root_addr;
return @intToPtr([*]u8, adjusted_addr)[0..n];
@@ -126,8 +124,7 @@ pub const DirectAllocator = struct {
const base_addr = @ptrToInt(old_mem.ptr);
const old_addr_end = base_addr + old_mem.len;
const new_addr_end = base_addr + new_size;
- const rem = @rem(new_addr_end, os.page_size);
- const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);
+ const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size);
if (old_addr_end > new_addr_end_rounded) {
_ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);
}
diff --git a/std/index.zig b/std/index.zig
index 80d1e46bb6..2a63244004 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -33,8 +33,8 @@ pub const io = @import("io.zig");
pub const json = @import("json.zig");
pub const macho = @import("macho.zig");
pub const math = @import("math/index.zig");
-pub const meta = @import("meta/index.zig");
pub const mem = @import("mem.zig");
+pub const meta = @import("meta/index.zig");
pub const net = @import("net.zig");
pub const os = @import("os/index.zig");
pub const pdb = @import("pdb.zig");
diff --git a/std/io.zig b/std/io.zig
index c8701aeda6..81d90def6e 100644
--- a/std/io.zig
+++ b/std/io.zig
@@ -912,7 +912,7 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
}
/// Flush any remaining bits to the stream.
- pub fn flushBits(self: *Self) !void {
+ pub fn flushBits(self: *Self) Error!void {
if (self.bit_count == 0) return;
try self.out_stream.writeByte(self.bit_buffer);
self.bit_buffer = 0;
@@ -1079,7 +1079,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
}
//@BUG: inferred error issue. See: #1386
- fn deserializeInt(self: *Self, comptime T: type) (Stream.Error || error{EndOfStream})!T {
+ fn deserializeInt(self: *Self, comptime T: type) (Error || error{EndOfStream})!T {
comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));
const u8_bit_count = 8;
@@ -1287,11 +1287,11 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
}
/// Flushes any unwritten bits to the stream
- pub fn flush(self: *Self) Stream.Error!void {
+ pub fn flush(self: *Self) Error!void {
if (is_packed) return self.out_stream.flushBits();
}
- fn serializeInt(self: *Self, value: var) !void {
+ fn serializeInt(self: *Self, value: var) Error!void {
const T = @typeOf(value);
comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));
@@ -1323,7 +1323,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
}
/// Serializes the passed value into the stream
- pub fn serialize(self: *Self, value: var) !void {
+ pub fn serialize(self: *Self, value: var) Error!void {
const T = comptime @typeOf(value);
if (comptime trait.isIndexable(T)) {
diff --git a/std/io_test.zig b/std/io_test.zig
index 0bee0ddaf0..9a0687ec69 100644
--- a/std/io_test.zig
+++ b/std/io_test.zig
@@ -357,6 +357,15 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa
const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
assert(in.pos == if (is_packed) total_packed_bytes else total_bytes);
+
+ //Verify that empty error set works with serializer.
+ //deserializer is covered by SliceInStream
+ const NullError = io.NullOutStream.Error;
+ var null_out = io.NullOutStream.init();
+ var null_out_stream = &null_out.stream;
+ var null_serializer = io.Serializer(endian, is_packed, NullError).init(null_out_stream);
+ try null_serializer.serialize(data_mem[0..]);
+ try null_serializer.flush();
}
test "Serializer/Deserializer Int" {
@@ -568,4 +577,4 @@ test "Deserializer bad data" {
try testBadData(builtin.Endian.Little, false);
try testBadData(builtin.Endian.Big, true);
try testBadData(builtin.Endian.Little, true);
-} \ No newline at end of file
+}
diff --git a/std/mem.zig b/std/mem.zig
index 26ae4ef089..a6cbae744f 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -882,42 +882,40 @@ pub const SplitIterator = struct {
}
};
-/// Naively combines a series of strings with a separator.
+/// Naively combines a series of slices with a separator.
/// Allocates memory for the result, which must be freed by the caller.
-pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 {
- comptime assert(strings.len >= 1);
- var total_strings_len: usize = strings.len; // 1 sep per string
- {
- comptime var string_i = 0;
- inline while (string_i < strings.len) : (string_i += 1) {
- const arg = ([]const u8)(strings[string_i]);
- total_strings_len += arg.len;
- }
- }
+pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 {
+ if (slices.len == 0) return (([*]u8)(undefined))[0..0];
+
+ const total_len = blk: {
+ var sum: usize = separator.len * (slices.len - 1);
+ for (slices) |slice|
+ sum += slice.len;
+ break :blk sum;
+ };
- const buf = try allocator.alloc(u8, total_strings_len);
+ const buf = try allocator.alloc(u8, total_len);
errdefer allocator.free(buf);
- var buf_index: usize = 0;
- comptime var string_i = 0;
- inline while (true) {
- const arg = ([]const u8)(strings[string_i]);
- string_i += 1;
- copy(u8, buf[buf_index..], arg);
- buf_index += arg.len;
- if (string_i >= strings.len) break;
- if (buf[buf_index - 1] != sep) {
- buf[buf_index] = sep;
- buf_index += 1;
- }
+ copy(u8, buf, slices[0]);
+ var buf_index: usize = slices[0].len;
+ for (slices[1..]) |slice| {
+ copy(u8, buf[buf_index..], separator);
+ buf_index += separator.len;
+ copy(u8, buf[buf_index..], slice);
+ buf_index += slice.len;
}
- return allocator.shrink(u8, buf, buf_index);
+ // No need for shrink since buf is exactly the correct size.
+ return buf;
}
test "mem.join" {
- assert(eql(u8, try join(debug.global_allocator, ',', "a", "b", "c"), "a,b,c"));
- assert(eql(u8, try join(debug.global_allocator, ',', "a"), "a"));
+ var buf: [1024]u8 = undefined;
+ const a = &std.heap.FixedBufferAllocator.init(&buf).allocator;
+ assert(eql(u8, try join(a, ",", [][]const u8{ "a", "b", "c" }), "a,b,c"));
+ assert(eql(u8, try join(a, ",", [][]const u8{"a"}), "a"));
+ assert(eql(u8, try join(a, ",", [][]const u8{ "a", "", "b", "", "c" }), "a,,b,,c"));
}
test "testStringEquality" {
@@ -1366,3 +1364,23 @@ test "std.mem.subArrayPtr" {
sub2[1] = 'X';
debug.assert(std.mem.eql(u8, a2, "abcXef"));
}
+
+/// Round an address up to the nearest aligned address
+pub fn alignForward(addr: usize, alignment: usize) usize {
+ return (addr + alignment - 1) & ~(alignment - 1);
+}
+
+test "std.mem.alignForward" {
+ debug.assertOrPanic(alignForward(1, 1) == 1);
+ debug.assertOrPanic(alignForward(2, 1) == 2);
+ debug.assertOrPanic(alignForward(1, 2) == 2);
+ debug.assertOrPanic(alignForward(2, 2) == 2);
+ debug.assertOrPanic(alignForward(3, 2) == 4);
+ debug.assertOrPanic(alignForward(4, 2) == 4);
+ debug.assertOrPanic(alignForward(7, 8) == 8);
+ debug.assertOrPanic(alignForward(8, 8) == 8);
+ debug.assertOrPanic(alignForward(9, 8) == 16);
+ debug.assertOrPanic(alignForward(15, 8) == 16);
+ debug.assertOrPanic(alignForward(16, 8) == 16);
+ debug.assertOrPanic(alignForward(17, 8) == 24);
+}
diff --git a/std/os/child_process.zig b/std/os/child_process.zig
index 7aa8582369..6635b76976 100644
--- a/std/os/child_process.zig
+++ b/std/os/child_process.zig
@@ -574,7 +574,7 @@ pub const ChildProcess = struct {
// to match posix semantics
const app_name = x: {
if (self.cwd) |cwd| {
- const resolved = try os.path.resolve(self.allocator, cwd, self.argv[0]);
+ const resolved = try os.path.resolve(self.allocator, [][]const u8{ cwd, self.argv[0] });
defer self.allocator.free(resolved);
break :x try cstr.addNullByte(self.allocator, resolved);
} else {
@@ -597,10 +597,10 @@ pub const ChildProcess = struct {
var it = mem.tokenize(PATH, ";");
while (it.next()) |search_path| {
- const joined_path = try os.path.join(self.allocator, search_path, app_name);
+ const joined_path = try os.path.join(self.allocator, [][]const u8{ search_path, app_name });
defer self.allocator.free(joined_path);
- const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
+ const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, joined_path);
defer self.allocator.free(joined_path_w);
if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
@@ -610,6 +610,9 @@ pub const ChildProcess = struct {
} else {
return err;
}
+ } else {
+ // Every other error would have been returned earlier.
+ return error.FileNotFound;
}
};
diff --git a/std/os/get_app_data_dir.zig b/std/os/get_app_data_dir.zig
index ae133bb4b1..f5e0b78eec 100644
--- a/std/os/get_app_data_dir.zig
+++ b/std/os/get_app_data_dir.zig
@@ -30,7 +30,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
error.OutOfMemory => return error.OutOfMemory,
};
defer allocator.free(global_dir);
- return os.path.join(allocator, global_dir, appname);
+ return os.path.join(allocator, [][]const u8{ global_dir, appname });
},
os.windows.E_OUTOFMEMORY => return error.OutOfMemory,
else => return error.AppDataDirUnavailable,
@@ -41,14 +41,14 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
// TODO look in /etc/passwd
return error.AppDataDirUnavailable;
};
- return os.path.join(allocator, home_dir, "Library", "Application Support", appname);
+ return os.path.join(allocator, [][]const u8{ home_dir, "Library", "Application Support", appname });
},
builtin.Os.linux, builtin.Os.freebsd => {
const home_dir = os.getEnvPosix("HOME") orelse {
// TODO look in /etc/passwd
return error.AppDataDirUnavailable;
};
- return os.path.join(allocator, home_dir, ".local", "share", appname);
+ return os.path.join(allocator, [][]const u8{ home_dir, ".local", "share", appname });
},
else => @compileError("Unsupported OS"),
}
@@ -67,4 +67,3 @@ test "std.os.getAppDataDir" {
// We can't actually validate the result
_ = getAppDataDir(allocator, "zig") catch return;
}
-
diff --git a/std/os/index.zig b/std/os/index.zig
index 451c0a3436..8e9876c36b 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -8,6 +8,10 @@ const is_posix = switch (builtin.os) {
};
const os = @This();
+comptime {
+ assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir
+}
+
test "std.os" {
_ = @import("child_process.zig");
_ = @import("darwin.zig");
@@ -692,12 +696,7 @@ pub fn getBaseAddress() usize {
return base;
}
const phdr = linuxGetAuxVal(std.elf.AT_PHDR);
- const ElfHeader = switch (@sizeOf(usize)) {
- 4 => std.elf.Elf32_Ehdr,
- 8 => std.elf.Elf64_Ehdr,
- else => @compileError("Unsupported architecture"),
- };
- return phdr - @sizeOf(ElfHeader);
+ return phdr - @sizeOf(std.elf.Ehdr);
},
builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header),
builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),
@@ -1285,7 +1284,7 @@ pub fn makeDirPosix(dir_path: []const u8) !void {
/// already exists and is a directory.
/// TODO determine if we can remove the allocator requirement from this function
pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
- const resolved_path = try path.resolve(allocator, full_path);
+ const resolved_path = try path.resolve(allocator, [][]const u8{full_path});
defer allocator.free(resolved_path);
var end_index: usize = resolved_path.len;
@@ -2305,18 +2304,17 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
switch (builtin.os) {
Os.linux => return readLink(out_buffer, "/proc/self/exe"),
Os.freebsd => {
- var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1};
+ var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1 };
var out_len: usize = out_buffer.len;
const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0));
- if (err == 0 ) return mem.toSlice(u8, out_buffer);
+ if (err == 0) return mem.toSlice(u8, out_buffer);
return switch (err) {
posix.EFAULT => error.BadAdress,
posix.EPERM => error.PermissionDenied,
else => unexpectedErrorPosix(err),
};
-
},
Os.windows => {
var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
@@ -2908,14 +2906,15 @@ pub const Thread = struct {
pub const Data = if (use_pthreads)
struct {
handle: Thread.Handle,
- stack_addr: usize,
- stack_len: usize,
+ mmap_addr: usize,
+ mmap_len: usize,
}
else switch (builtin.os) {
builtin.Os.linux => struct {
handle: Thread.Handle,
- stack_addr: usize,
- stack_len: usize,
+ mmap_addr: usize,
+ mmap_len: usize,
+ tls_end_addr: usize,
},
builtin.Os.windows => struct {
handle: Thread.Handle,
@@ -2955,7 +2954,7 @@ pub const Thread = struct {
posix.EDEADLK => unreachable,
else => unreachable,
}
- assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
+ assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
} else switch (builtin.os) {
builtin.Os.linux => {
while (true) {
@@ -2969,7 +2968,7 @@ pub const Thread = struct {
else => unreachable,
}
}
- assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
+ assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
},
builtin.Os.windows => {
assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
@@ -3008,6 +3007,9 @@ pub const SpawnThreadError = error{
Unexpected,
};
+pub var linux_tls_phdr: ?*std.elf.Phdr = null;
+pub var linux_tls_img_src: [*]const u8 = undefined; // defined if linux_tls_phdr is
+
/// caller must call wait on the returned thread
/// fn startFn(@typeOf(context)) T
/// where T is u8, noreturn, void, or !void
@@ -3097,42 +3099,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
- const mmap_len = default_stack_size;
- const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
- if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;
- errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);
+ var stack_end_offset: usize = undefined;
+ var thread_start_offset: usize = undefined;
+ var context_start_offset: usize = undefined;
+ var tls_start_offset: usize = undefined;
+ const mmap_len = blk: {
+ // First in memory will be the stack, which grows downwards.
+ var l: usize = mem.alignForward(default_stack_size, os.page_size);
+ stack_end_offset = l;
+ // Above the stack, so that it can be in the same mmap call, put the Thread object.
+ l = mem.alignForward(l, @alignOf(Thread));
+ thread_start_offset = l;
+ l += @sizeOf(Thread);
+ // Next, the Context object.
+ if (@sizeOf(Context) != 0) {
+ l = mem.alignForward(l, @alignOf(Context));
+ context_start_offset = l;
+ l += @sizeOf(Context);
+ }
+ // Finally, the Thread Local Storage, if any.
+ if (!Thread.use_pthreads) {
+ if (linux_tls_phdr) |tls_phdr| {
+ l = mem.alignForward(l, tls_phdr.p_align);
+ tls_start_offset = l;
+ l += tls_phdr.p_memsz;
+ }
+ }
+ break :blk l;
+ };
+ const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
+ if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory;
+ errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0);
+
+ const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset));
+ thread_ptr.data.mmap_addr = mmap_addr;
+ thread_ptr.data.mmap_len = mmap_len;
- var stack_end: usize = stack_addr + mmap_len;
var arg: usize = undefined;
if (@sizeOf(Context) != 0) {
- stack_end -= @sizeOf(Context);
- stack_end -= stack_end % @alignOf(Context);
- assert(stack_end >= stack_addr);
- const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, stack_end));
+ arg = mmap_addr + context_start_offset;
+ const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg));
context_ptr.* = context;
- arg = stack_end;
}
- stack_end -= @sizeOf(Thread);
- stack_end -= stack_end % @alignOf(Thread);
- assert(stack_end >= stack_addr);
- const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, stack_end));
-
- thread_ptr.data.stack_addr = stack_addr;
- thread_ptr.data.stack_len = mmap_len;
-
- if (builtin.os == builtin.Os.windows) {
- // use windows API directly
- @compileError("TODO support spawnThread for Windows");
- } else if (Thread.use_pthreads) {
+ if (Thread.use_pthreads) {
// use pthreads
var attr: c.pthread_attr_t = undefined;
if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
defer assert(c.pthread_attr_destroy(&attr) == 0);
- // align to page
- stack_end -= stack_end % os.page_size;
- assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0);
+ assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0);
const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));
switch (err) {
@@ -3143,10 +3159,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
else => return unexpectedErrorPosix(@intCast(usize, err)),
}
} else if (builtin.os == builtin.Os.linux) {
- // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly
- const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
- const newtls: usize = 0;
- const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
+ var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND |
+ posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID |
+ posix.CLONE_DETACHED;
+ var newtls: usize = undefined;
+ if (linux_tls_phdr) |tls_phdr| {
+ @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), linux_tls_img_src, tls_phdr.p_filesz);
+ thread_ptr.data.tls_end_addr = mmap_addr + mmap_len;
+ newtls = @ptrToInt(&thread_ptr.data.tls_end_addr);
+ flags |= posix.CLONE_SETTLS;
+ }
+ const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
const err = posix.getErrno(rc);
switch (err) {
0 => return thread_ptr,
diff --git a/std/os/path.zig b/std/os/path.zig
index 0b960fa2da..266a77b97c 100644
--- a/std/os/path.zig
+++ b/std/os/path.zig
@@ -33,40 +33,103 @@ pub fn isSep(byte: u8) bool {
}
}
+/// This is different from mem.join in that the separator will not be repeated if
+/// it is found at the end or beginning of a pair of consecutive paths.
+fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u8 {
+ if (paths.len == 0) return (([*]u8)(undefined))[0..0];
+
+ const total_len = blk: {
+ var sum: usize = paths[0].len;
+ var i: usize = 1;
+ while (i < paths.len) : (i += 1) {
+ const prev_path = paths[i - 1];
+ const this_path = paths[i];
+ const prev_sep = (prev_path.len != 0 and prev_path[prev_path.len - 1] == separator);
+ const this_sep = (this_path.len != 0 and this_path[0] == separator);
+ sum += @boolToInt(!prev_sep and !this_sep);
+ sum += if (prev_sep and this_sep) this_path.len - 1 else this_path.len;
+ }
+ break :blk sum;
+ };
+
+ const buf = try allocator.alloc(u8, total_len);
+ errdefer allocator.free(buf);
+
+ mem.copy(u8, buf, paths[0]);
+ var buf_index: usize = paths[0].len;
+ var i: usize = 1;
+ while (i < paths.len) : (i += 1) {
+ const prev_path = paths[i - 1];
+ const this_path = paths[i];
+ const prev_sep = (prev_path.len != 0 and prev_path[prev_path.len - 1] == separator);
+ const this_sep = (this_path.len != 0 and this_path[0] == separator);
+ if (!prev_sep and !this_sep) {
+ buf[buf_index] = separator;
+ buf_index += 1;
+ }
+ const adjusted_path = if (prev_sep and this_sep) this_path[1..] else this_path;
+ mem.copy(u8, buf[buf_index..], adjusted_path);
+ buf_index += adjusted_path.len;
+ }
+
+ // No need for shrink since buf is exactly the correct size.
+ return buf;
+}
+
+pub const join = if (is_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.
-pub fn join(allocator: *Allocator, paths: ...) ![]u8 {
- if (is_windows) {
- return joinWindows(allocator, paths);
- } else {
- return joinPosix(allocator, paths);
- }
+pub fn joinWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
+ return joinSep(allocator, sep_windows, paths);
+}
+
+/// Naively combines a series of paths with the native path seperator.
+/// Allocates memory for the result, which must be freed by the caller.
+pub fn joinPosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
+ return joinSep(allocator, sep_posix, paths);
}
-pub fn joinWindows(allocator: *Allocator, paths: ...) ![]u8 {
- return mem.join(allocator, sep_windows, paths);
+fn testJoinWindows(paths: []const []const u8, expected: []const u8) void {
+ var buf: [1024]u8 = undefined;
+ const a = &std.heap.FixedBufferAllocator.init(&buf).allocator;
+ const actual = joinWindows(a, paths) catch @panic("fail");
+ debug.assertOrPanic(mem.eql(u8, actual, expected));
}
-pub fn joinPosix(allocator: *Allocator, paths: ...) ![]u8 {
- return mem.join(allocator, sep_posix, paths);
+fn testJoinPosix(paths: []const []const u8, expected: []const u8) void {
+ var buf: [1024]u8 = undefined;
+ const a = &std.heap.FixedBufferAllocator.init(&buf).allocator;
+ const actual = joinPosix(a, paths) catch @panic("fail");
+ debug.assertOrPanic(mem.eql(u8, actual, expected));
}
test "os.path.join" {
- assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\a\\b", "c"), "c:\\a\\b\\c"));
- assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\a\\b\\", "c"), "c:\\a\\b\\c"));
+ testJoinWindows([][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c");
+ testJoinWindows([][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c");
+ testJoinWindows([][]const u8{ "c:\\a\\b\\", "c" }, "c:\\a\\b\\c");
- assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\", "a", "b\\", "c"), "c:\\a\\b\\c"));
- assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\a\\", "b\\", "c"), "c:\\a\\b\\c"));
+ testJoinWindows([][]const u8{ "c:\\", "a", "b\\", "c" }, "c:\\a\\b\\c");
+ testJoinWindows([][]const u8{ "c:\\a\\", "b\\", "c" }, "c:\\a\\b\\c");
- assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig"), "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig"));
+ testJoinWindows(
+ [][]const u8{ "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig" },
+ "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig",
+ );
- assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/b", "c"), "/a/b/c"));
- assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/b/", "c"), "/a/b/c"));
+ testJoinPosix([][]const u8{ "/a/b", "c" }, "/a/b/c");
+ testJoinPosix([][]const u8{ "/a/b/", "c" }, "/a/b/c");
- assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/", "a", "b/", "c"), "/a/b/c"));
- assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/", "b/", "c"), "/a/b/c"));
+ testJoinPosix([][]const u8{ "/", "a", "b/", "c" }, "/a/b/c");
+ testJoinPosix([][]const u8{ "/a/", "b/", "c" }, "/a/b/c");
- assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/home/andy/dev/zig/build/lib/zig/std", "io.zig"), "/home/andy/dev/zig/build/lib/zig/std/io.zig"));
+ testJoinPosix(
+ [][]const u8{ "/home/andy/dev/zig/build/lib/zig/std", "io.zig" },
+ "/home/andy/dev/zig/build/lib/zig/std/io.zig",
+ );
+
+ testJoinPosix([][]const u8{ "a", "/c" }, "a/c");
+ testJoinPosix([][]const u8{ "a/", "/c" }, "a/c");
}
pub fn isAbsolute(path: []const u8) bool {
@@ -312,18 +375,8 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool {
return true;
}
-/// Converts the command line arguments into a slice and calls `resolveSlice`.
-pub fn resolve(allocator: *Allocator, args: ...) ![]u8 {
- var paths: [args.len][]const u8 = undefined;
- comptime var arg_i = 0;
- inline while (arg_i < args.len) : (arg_i += 1) {
- paths[arg_i] = args[arg_i];
- }
- return resolveSlice(allocator, paths);
-}
-
/// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`.
-pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 {
+pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 {
if (is_windows) {
return resolveWindows(allocator, paths);
} else {
@@ -602,7 +655,10 @@ test "os.path.resolveWindows" {
const parsed_cwd = windowsParsePath(cwd);
{
const result = testResolveWindows([][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" });
- const expected = try join(debug.global_allocator, parsed_cwd.disk_designator, "usr\\local\\lib\\zig\\std\\array_list.zig");
+ const expected = try join(debug.global_allocator, [][]const u8{
+ parsed_cwd.disk_designator,
+ "usr\\local\\lib\\zig\\std\\array_list.zig",
+ });
if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
expected[0] = asciiUpper(parsed_cwd.disk_designator[0]);
}
@@ -610,7 +666,10 @@ test "os.path.resolveWindows" {
}
{
const result = testResolveWindows([][]const u8{ "usr/local", "lib\\zig" });
- const expected = try join(debug.global_allocator, cwd, "usr\\local\\lib\\zig");
+ const expected = try join(debug.global_allocator, [][]const u8{
+ cwd,
+ "usr\\local\\lib\\zig",
+ });
if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
expected[0] = asciiUpper(parsed_cwd.disk_designator[0]);
}
diff --git a/std/os/test.zig b/std/os/test.zig
index f14cf47786..bd9148d1b1 100644
--- a/std/os/test.zig
+++ b/std/os/test.zig
@@ -105,3 +105,19 @@ test "AtomicFile" {
try os.deleteFile(test_out_file);
}
+
+test "thread local storage" {
+ if (builtin.single_threaded) return error.SkipZigTest;
+ const thread1 = try std.os.spawnThread({}, testTls);
+ const thread2 = try std.os.spawnThread({}, testTls);
+ testTls({});
+ thread1.wait();
+ thread2.wait();
+}
+
+threadlocal var x: i32 = 1234;
+fn testTls(context: void) void {
+ if (x != 1234) @panic("bad start value");
+ x += 1;
+ if (x != 1235) @panic("bad end value");
+}
diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig
index 3f19905835..8e9ed8b8db 100644
--- a/std/os/windows/index.zig
+++ b/std/os/windows/index.zig
@@ -49,7 +49,10 @@ pub const UNICODE = false;
pub const WCHAR = u16;
pub const WORD = u16;
pub const LARGE_INTEGER = i64;
-pub const LONG = c_long;
+pub const ULONG = u32;
+pub const LONG = i32;
+pub const ULONGLONG = u64;
+pub const LONGLONG = i64;
pub const TRUE = 1;
pub const FALSE = 0;
@@ -380,3 +383,17 @@ pub const COORD = extern struct {
};
pub const CREATE_UNICODE_ENVIRONMENT = 1024;
+
+pub const TLS_OUT_OF_INDEXES = 4294967295;
+pub const IMAGE_TLS_DIRECTORY = extern struct {
+ StartAddressOfRawData: usize,
+ EndAddressOfRawData: usize,
+ AddressOfIndex: usize,
+ AddressOfCallBacks: usize,
+ SizeOfZeroFill: u32,
+ Characteristics: u32,
+};
+pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY;
+pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY;
+
+pub const PIMAGE_TLS_CALLBACK = ?extern fn(PVOID, DWORD, PVOID) void;
diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig
index 66b9552c5f..ce8443de02 100644
--- a/std/os/windows/kernel32.zig
+++ b/std/os/windows/kernel32.zig
@@ -164,6 +164,10 @@ pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
+pub extern "kernel32" stdcallcc fn TlsAlloc() DWORD;
+
+pub extern "kernel32" stdcallcc fn TlsFree(dwTlsIndex: DWORD) BOOL;
+
pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn WriteFile(
diff --git a/std/os/windows/tls.zig b/std/os/windows/tls.zig
new file mode 100644
index 0000000000..9e62a7c5c6
--- /dev/null
+++ b/std/os/windows/tls.zig
@@ -0,0 +1,36 @@
+const std = @import("../../index.zig");
+
+export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES;
+export var _tls_start: u8 linksection(".tls") = 0;
+export var _tls_end: u8 linksection(".tls$ZZZ") = 0;
+export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null;
+export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null;
+
+// TODO this is how I would like it to be expressed
+// TODO also note, ReactOS has a +1 on StartAddressOfRawData and AddressOfCallBacks. Investigate
+// why they do that.
+//export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY {
+// .StartAddressOfRawData = @ptrToInt(&_tls_start),
+// .EndAddressOfRawData = @ptrToInt(&_tls_end),
+// .AddressOfIndex = @ptrToInt(&_tls_index),
+// .AddressOfCallBacks = @ptrToInt(__xl_a),
+// .SizeOfZeroFill = 0,
+// .Characteristics = 0,
+//};
+// This is the workaround because we can't do @ptrToInt at comptime like that.
+pub const IMAGE_TLS_DIRECTORY = extern struct {
+ StartAddressOfRawData: *c_void,
+ EndAddressOfRawData: *c_void,
+ AddressOfIndex: *c_void,
+ AddressOfCallBacks: *c_void,
+ SizeOfZeroFill: u32,
+ Characteristics: u32,
+};
+export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY {
+ .StartAddressOfRawData = &_tls_start,
+ .EndAddressOfRawData = &_tls_end,
+ .AddressOfIndex = &_tls_index,
+ .AddressOfCallBacks = &__xl_a,
+ .SizeOfZeroFill = 0,
+ .Characteristics = 0,
+};
diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig
index 129bde913f..6dcc90b372 100644
--- a/std/special/bootstrap.zig
+++ b/std/special/bootstrap.zig
@@ -4,6 +4,7 @@
const root = @import("@root");
const std = @import("std");
const builtin = @import("builtin");
+const assert = std.debug.assert;
var argc_ptr: [*]usize = undefined;
@@ -44,7 +45,9 @@ nakedcc fn _start() noreturn {
extern fn WinMainCRTStartup() noreturn {
@setAlignStack(16);
-
+ if (!builtin.single_threaded) {
+ _ = @import("../os/windows/tls.zig");
+ }
std.os.windows.ExitProcess(callMain());
}
@@ -61,9 +64,23 @@ fn posixCallMainAndExit() noreturn {
while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];
if (builtin.os == builtin.Os.linux) {
- const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1);
- std.os.linux_elf_aux_maybe = @ptrCast([*]std.elf.Auxv, auxv);
- std.debug.assert(std.os.linuxGetAuxVal(std.elf.AT_PAGESZ) == std.os.page_size);
+ // Scan auxiliary vector.
+ const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1);
+ std.os.linux_elf_aux_maybe = auxv;
+ var i: usize = 0;
+ var at_phdr: usize = 0;
+ var at_phnum: usize = 0;
+ var at_phent: usize = 0;
+ while (auxv[i].a_un.a_val != 0) : (i += 1) {
+ switch (auxv[i].a_type) {
+ std.elf.AT_PAGESZ => assert(auxv[i].a_un.a_val == std.os.page_size),
+ std.elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val,
+ std.elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val,
+ std.elf.AT_PHENT => at_phent = auxv[i].a_un.a_val,
+ else => {},
+ }
+ }
+ if (!builtin.single_threaded) linuxInitializeThreadLocalStorage(at_phdr, at_phnum, at_phent);
}
std.os.posix.exit(callMainWithArgs(argc, argv, envp));
@@ -116,3 +133,41 @@ inline fn callMain() u8 {
else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),
}
}
+
+var tls_end_addr: usize = undefined;
+const main_thread_tls_align = 32;
+var main_thread_tls_bytes: [64]u8 align(main_thread_tls_align) = [1]u8{0} ** 64;
+
+fn linuxInitializeThreadLocalStorage(at_phdr: usize, at_phnum: usize, at_phent: usize) void {
+ var phdr_addr = at_phdr;
+ var n = at_phnum;
+ var base: usize = 0;
+ while (n != 0) : ({n -= 1; phdr_addr += at_phent;}) {
+ const phdr = @intToPtr(*std.elf.Phdr, phdr_addr);
+ // TODO look for PT_DYNAMIC when we have https://github.com/ziglang/zig/issues/1917
+ switch (phdr.p_type) {
+ std.elf.PT_PHDR => base = at_phdr - phdr.p_vaddr,
+ std.elf.PT_TLS => std.os.linux_tls_phdr = phdr,
+ else => continue,
+ }
+ }
+ const tls_phdr = std.os.linux_tls_phdr orelse return;
+ std.os.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr);
+ assert(main_thread_tls_bytes.len >= tls_phdr.p_memsz); // not enough preallocated Thread Local Storage
+ assert(main_thread_tls_align >= tls_phdr.p_align); // preallocated Thread Local Storage not aligned enough
+ @memcpy(&main_thread_tls_bytes, std.os.linux_tls_img_src, tls_phdr.p_filesz);
+ tls_end_addr = @ptrToInt(&main_thread_tls_bytes) + tls_phdr.p_memsz;
+ linuxSetThreadArea(@ptrToInt(&tls_end_addr));
+}
+
+fn linuxSetThreadArea(addr: usize) void {
+ switch (builtin.arch) {
+ builtin.Arch.x86_64 => {
+ const ARCH_SET_FS = 0x1002;
+ const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, ARCH_SET_FS, addr);
+ // acrh_prctl is documented to never fail
+ assert(rc == 0);
+ },
+ else => @compileError("Unsupported architecture"),
+ }
+}