mirror of
https://github.com/ziglang/zig.git
synced 2025-12-15 02:33:07 +00:00
Merge remote-tracking branch 'origin/master' into llvm7
This commit is contained in:
commit
dbde8254d0
@ -447,13 +447,17 @@ set(ZIG_STD_FILES
|
||||
"c/index.zig"
|
||||
"c/linux.zig"
|
||||
"c/windows.zig"
|
||||
"coff.zig"
|
||||
"crypto/blake2.zig"
|
||||
"crypto/chacha20.zig"
|
||||
"crypto/hmac.zig"
|
||||
"crypto/index.zig"
|
||||
"crypto/md5.zig"
|
||||
"crypto/sha1.zig"
|
||||
"crypto/sha2.zig"
|
||||
"crypto/sha3.zig"
|
||||
"crypto/poly1305.zig"
|
||||
"crypto/x25519.zig"
|
||||
"cstr.zig"
|
||||
"debug/failing_allocator.zig"
|
||||
"debug/index.zig"
|
||||
@ -579,12 +583,12 @@ set(ZIG_STD_FILES
|
||||
"os/windows/error.zig"
|
||||
"os/windows/index.zig"
|
||||
"os/windows/kernel32.zig"
|
||||
"os/windows/ntdll.zig"
|
||||
"os/windows/ole32.zig"
|
||||
"os/windows/shell32.zig"
|
||||
"os/windows/shlwapi.zig"
|
||||
"os/windows/user32.zig"
|
||||
"os/windows/util.zig"
|
||||
"os/zen.zig"
|
||||
"pdb.zig"
|
||||
"rand/index.zig"
|
||||
"rand/ziggurat.zig"
|
||||
"segmented_list.zig"
|
||||
|
||||
@ -40,11 +40,11 @@ pub fn main() !void {
|
||||
var out_file = try os.File.openWrite(out_file_name);
|
||||
defer out_file.close();
|
||||
|
||||
var file_in_stream = io.FileInStream.init(&in_file);
|
||||
var file_in_stream = io.FileInStream.init(in_file);
|
||||
|
||||
const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size);
|
||||
|
||||
var file_out_stream = io.FileOutStream.init(&out_file);
|
||||
var file_out_stream = io.FileOutStream.init(out_file);
|
||||
var buffered_out_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream);
|
||||
|
||||
var tokenizer = Tokenizer.init(in_file_name, input_file_bytes);
|
||||
|
||||
@ -566,7 +566,7 @@ const c_string_literal =
|
||||
{#header_close#}
|
||||
{#header_close#}
|
||||
{#header_open|Assignment#}
|
||||
<p>Use <code>const</code> to assign a value to an identifier:</p>
|
||||
<p>Use the <code>const</code> keyword to assign a value to an identifier:</p>
|
||||
{#code_begin|test_err|cannot assign to constant#}
|
||||
const x = 1234;
|
||||
|
||||
@ -582,7 +582,8 @@ test "assignment" {
|
||||
foo();
|
||||
}
|
||||
{#code_end#}
|
||||
<p>If you need a variable that you can modify, use <code>var</code>:</p>
|
||||
<p><code>const</code> applies to all of the bytes that the identifier immediately addresses. {#link|Pointers#} have their own const-ness.</p>
|
||||
<p>If you need a variable that you can modify, use the <code>var</code> keyword:</p>
|
||||
{#code_begin|test#}
|
||||
const assert = @import("std").debug.assert;
|
||||
|
||||
@ -1918,6 +1919,32 @@ test "linked list" {
|
||||
assert(list2.first.?.data == 1234);
|
||||
}
|
||||
{#code_end#}
|
||||
{#header_open|struct Naming#}
|
||||
<p>Since all structs are anonymous, Zig infers the type name based on a few rules.</p>
|
||||
<ul>
|
||||
<li>If the struct is in the initialization expression of a variable, it gets named after
|
||||
that variable.</li>
|
||||
<li>If the struct is in the <code>return</code> expression, it gets named after
|
||||
the function it is returning from, with the parameter values serialized.</li>
|
||||
<li>Otherwise, the struct gets a same such as <code>(anonymous struct at file.zig:7:38)</code>.</li>
|
||||
</ul>
|
||||
{#code_begin|exe|struct_name#}
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() void {
|
||||
const Foo = struct {};
|
||||
std.debug.warn("variable: {}\n", @typeName(Foo));
|
||||
std.debug.warn("anonymous: {}\n", @typeName(struct {}));
|
||||
std.debug.warn("function: {}\n", @typeName(List(i32)));
|
||||
}
|
||||
|
||||
fn List(comptime T: type) type {
|
||||
return struct {
|
||||
x: T,
|
||||
};
|
||||
}
|
||||
{#code_end#}
|
||||
{#header_close#}
|
||||
{#see_also|comptime|@fieldParentPtr#}
|
||||
{#header_close#}
|
||||
{#header_open|enum#}
|
||||
@ -2179,6 +2206,39 @@ test "@tagName" {
|
||||
sorts the order of the tag and union field by the largest alignment.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|blocks#}
|
||||
<p>
|
||||
Blocks are used to limit the scope of variable declarations:
|
||||
</p>
|
||||
{#code_begin|test_err|undeclared identifier#}
|
||||
test "access variable after block scope" {
|
||||
{
|
||||
var x: i32 = 1;
|
||||
}
|
||||
x += 1;
|
||||
}
|
||||
{#code_end#}
|
||||
<p>Blocks are expressions. When labeled, <code>break</code> can be used
|
||||
to return a value from the block:
|
||||
</p>
|
||||
{#code_begin|test#}
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
test "labeled break from labeled block expression" {
|
||||
var y: i32 = 123;
|
||||
|
||||
const x = blk: {
|
||||
y += 1;
|
||||
break :blk y;
|
||||
};
|
||||
assert(x == 124);
|
||||
assert(y == 124);
|
||||
}
|
||||
{#code_end#}
|
||||
<p>Here, <code>blk</code> can be any name.</p>
|
||||
{#see_also|Labeled while|Labeled for#}
|
||||
{#header_close#}
|
||||
{#header_open|switch#}
|
||||
{#code_begin|test|switch#}
|
||||
const assert = @import("std").debug.assert;
|
||||
@ -2374,6 +2434,28 @@ fn rangeHasNumber(begin: usize, end: usize, number: usize) bool {
|
||||
} else false;
|
||||
}
|
||||
{#code_end#}
|
||||
{#header_open|Labeled while#}
|
||||
<p>When a <code>while</code> loop is labeled, it can be referenced from a <code>break</code>
|
||||
or <code>continue</code> from within a nested loop:</p>
|
||||
{#code_begin|test#}
|
||||
test "nested break" {
|
||||
outer: while (true) {
|
||||
while (true) {
|
||||
break :outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "nested continue" {
|
||||
var i: usize = 0;
|
||||
outer: while (i < 10) : (i += 1) {
|
||||
while (true) {
|
||||
continue :outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
{#code_end#}
|
||||
{#header_close#}
|
||||
{#header_open|while with Optionals#}
|
||||
<p>
|
||||
Just like {#link|if#} expressions, while loops can take an optional as the
|
||||
@ -2560,6 +2642,37 @@ test "for else" {
|
||||
};
|
||||
}
|
||||
{#code_end#}
|
||||
{#header_open|Labeled for#}
|
||||
<p>When a <code>for</code> loop is labeled, it can be referenced from a <code>break</code>
|
||||
or <code>continue</code> from within a nested loop:</p>
|
||||
{#code_begin|test#}
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
test "nested break" {
|
||||
var count: usize = 0;
|
||||
outer: for ([]i32{ 1, 2, 3, 4, 5 }) |_| {
|
||||
for ([]i32{ 1, 2, 3, 4, 5 }) |_| {
|
||||
count += 1;
|
||||
break :outer;
|
||||
}
|
||||
}
|
||||
assert(count == 1);
|
||||
}
|
||||
|
||||
test "nested continue" {
|
||||
var count: usize = 0;
|
||||
outer: for ([]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| {
|
||||
for ([]i32{ 1, 2, 3, 4, 5 }) |_| {
|
||||
count += 1;
|
||||
continue :outer;
|
||||
}
|
||||
}
|
||||
|
||||
assert(count == 8);
|
||||
}
|
||||
{#code_end#}
|
||||
{#header_close#}
|
||||
{#header_open|inline for#}
|
||||
<p>
|
||||
For loops can be inlined. This causes the loop to be unrolled, which
|
||||
@ -7057,6 +7170,61 @@ const c = @cImport({
|
||||
});
|
||||
{#code_end#}
|
||||
{#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#}
|
||||
{#header_close#}
|
||||
{#header_open|Exporting a C Library#}
|
||||
<p>
|
||||
One of the primary use cases for Zig is exporting a library with the C ABI for other programming languages
|
||||
to call into. The <code>export</code> keyword in front of functions, variables, and types causes them to
|
||||
be part of the library API:
|
||||
</p>
|
||||
<p class="file">mathtest.zig</p>
|
||||
{#code_begin|syntax#}
|
||||
export fn add(a: i32, b: i32) i32 {
|
||||
return a + b;
|
||||
}
|
||||
{#code_end#}
|
||||
<p>To make a shared library:</p>
|
||||
<pre><code class="shell">$ zig build-lib mathtest.zig
|
||||
</code></pre>
|
||||
<p>To make a static library:</p>
|
||||
<pre><code class="shell">$ zig build-lib mathtest.zig --static
|
||||
</code></pre>
|
||||
<p>Here is an example with the {#link|Zig Build System#}:</p>
|
||||
<p class="file">test.c</p>
|
||||
<pre><code class="cpp">// This header is generated by zig from mathtest.zig
|
||||
#include "mathtest.h"
|
||||
#include <assert.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
assert(add(42, 1337) == 1379);
|
||||
return 0;
|
||||
}</code></pre>
|
||||
<p class="file">build.zig</p>
|
||||
{#code_begin|syntax#}
|
||||
const Builder = @import("std").build.Builder;
|
||||
|
||||
pub fn build(b: *Builder) void {
|
||||
const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
|
||||
|
||||
const exe = b.addCExecutable("test");
|
||||
exe.addCompileFlags([][]const u8{"-std=c99"});
|
||||
exe.addSourceFile("test.c");
|
||||
exe.linkLibrary(lib);
|
||||
|
||||
b.default_step.dependOn(&exe.step);
|
||||
|
||||
const run_cmd = b.addCommand(".", b.env_map, [][]const u8{exe.getOutputPath()});
|
||||
run_cmd.step.dependOn(&exe.step);
|
||||
|
||||
const test_step = b.step("test", "Test the program");
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
{#code_end#}
|
||||
<p class="file">terminal</p>
|
||||
<pre><code class="shell">$ zig build
|
||||
$ ./test
|
||||
$ echo $?
|
||||
0</code></pre>
|
||||
{#header_close#}
|
||||
{#header_open|Mixing Object Files#}
|
||||
<p>
|
||||
|
||||
@ -6,7 +6,7 @@ const os = std.os;
|
||||
|
||||
pub fn main() !void {
|
||||
var stdout_file = try io.getStdOut();
|
||||
var stdout_file_stream = io.FileOutStream.init(&stdout_file);
|
||||
var stdout_file_stream = io.FileOutStream.init(stdout_file);
|
||||
const stdout = &stdout_file_stream.stream;
|
||||
|
||||
try stdout.print("Welcome to the Guess Number Game in Zig.\n");
|
||||
|
||||
@ -272,7 +272,7 @@ pub const Msg = struct {
|
||||
try stream.write("\n");
|
||||
}
|
||||
|
||||
pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void {
|
||||
pub fn printToFile(msg: *const Msg, file: os.File, color: Color) !void {
|
||||
const color_on = switch (color) {
|
||||
Color.Auto => file.isTty(),
|
||||
Color.On => true,
|
||||
|
||||
@ -55,11 +55,11 @@ pub fn main() !void {
|
||||
const allocator = std.heap.c_allocator;
|
||||
|
||||
var stdout_file = try std.io.getStdOut();
|
||||
var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
|
||||
var stdout_out_stream = std.io.FileOutStream.init(stdout_file);
|
||||
stdout = &stdout_out_stream.stream;
|
||||
|
||||
stderr_file = try std.io.getStdErr();
|
||||
var stderr_out_stream = std.io.FileOutStream.init(&stderr_file);
|
||||
var stderr_out_stream = std.io.FileOutStream.init(stderr_file);
|
||||
stderr = &stderr_out_stream.stream;
|
||||
|
||||
const args = try os.argsAlloc(allocator);
|
||||
@ -491,7 +491,7 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
|
||||
stderr.print("Build {} compile errors:\n", count) catch os.exit(1);
|
||||
for (msgs) |msg| {
|
||||
defer msg.destroy();
|
||||
msg.printToFile(&stderr_file, color) catch os.exit(1);
|
||||
msg.printToFile(stderr_file, color) catch os.exit(1);
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -619,7 +619,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
|
||||
}
|
||||
|
||||
var stdin_file = try io.getStdIn();
|
||||
var stdin = io.FileInStream.init(&stdin_file);
|
||||
var stdin = io.FileInStream.init(stdin_file);
|
||||
|
||||
const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size);
|
||||
defer allocator.free(source_code);
|
||||
@ -635,7 +635,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
|
||||
const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
|
||||
defer msg.destroy();
|
||||
|
||||
try msg.printToFile(&stderr_file, color);
|
||||
try msg.printToFile(stderr_file, color);
|
||||
}
|
||||
if (tree.errors.len != 0) {
|
||||
os.exit(1);
|
||||
@ -772,7 +772,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
|
||||
const msg = try errmsg.Msg.createFromParseError(fmt.loop.allocator, parse_error, &tree, file_path);
|
||||
defer fmt.loop.allocator.destroy(msg);
|
||||
|
||||
try msg.printToFile(&stderr_file, fmt.color);
|
||||
try msg.printToFile(stderr_file, fmt.color);
|
||||
}
|
||||
if (tree.errors.len != 0) {
|
||||
fmt.any_error = true;
|
||||
|
||||
@ -185,7 +185,7 @@ pub const TestContext = struct {
|
||||
try stderr.write("build incorrectly failed:\n");
|
||||
for (msgs) |msg| {
|
||||
defer msg.destroy();
|
||||
try msg.printToFile(&stderr, errmsg.Color.Auto);
|
||||
try msg.printToFile(stderr, errmsg.Color.Auto);
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -234,7 +234,7 @@ pub const TestContext = struct {
|
||||
var stderr = try std.io.getStdErr();
|
||||
for (msgs) |msg| {
|
||||
defer msg.destroy();
|
||||
try msg.printToFile(&stderr, errmsg.Color.Auto);
|
||||
try msg.printToFile(stderr, errmsg.Color.Auto);
|
||||
}
|
||||
std.debug.warn("============\n");
|
||||
return error.TestFailed;
|
||||
|
||||
@ -43,6 +43,7 @@ struct IrAnalyze;
|
||||
struct IrExecutable {
|
||||
ZigList<IrBasicBlock *> basic_block_list;
|
||||
Buf *name;
|
||||
FnTableEntry *name_fn;
|
||||
size_t mem_slot_count;
|
||||
size_t next_debug_id;
|
||||
size_t *backward_branch_count;
|
||||
@ -1805,6 +1806,11 @@ struct VariableTableEntry {
|
||||
VarLinkage linkage;
|
||||
IrInstruction *decl_instruction;
|
||||
uint32_t align_bytes;
|
||||
|
||||
// In an inline loop, multiple variables may be created,
|
||||
// In this case, a reference to a variable should follow
|
||||
// this pointer to the redefined variable.
|
||||
VariableTableEntry *next_var;
|
||||
};
|
||||
|
||||
struct ErrorTableEntry {
|
||||
|
||||
@ -1575,7 +1575,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
|
||||
|
||||
switch (type_entry->id) {
|
||||
case TypeTableEntryIdInvalid:
|
||||
return g->builtin_types.entry_invalid;
|
||||
zig_unreachable();
|
||||
case TypeTableEntryIdUnreachable:
|
||||
case TypeTableEntryIdUndefined:
|
||||
case TypeTableEntryIdNull:
|
||||
@ -1680,14 +1680,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
|
||||
case TypeTableEntryIdBlock:
|
||||
case TypeTableEntryIdBoundFn:
|
||||
case TypeTableEntryIdMetaType:
|
||||
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
|
||||
add_node_error(g, fn_proto->return_type,
|
||||
buf_sprintf("return type '%s' not allowed in function with calling convention '%s'",
|
||||
buf_ptr(&fn_type_id.return_type->name),
|
||||
calling_convention_name(fn_type_id.cc)));
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
return get_generic_fn_type(g, &fn_type_id);
|
||||
case TypeTableEntryIdUnreachable:
|
||||
case TypeTableEntryIdVoid:
|
||||
case TypeTableEntryIdBool:
|
||||
@ -1703,6 +1695,11 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
|
||||
case TypeTableEntryIdUnion:
|
||||
case TypeTableEntryIdFn:
|
||||
case TypeTableEntryIdPromise:
|
||||
if ((err = type_ensure_zero_bits_known(g, fn_type_id.return_type)))
|
||||
return g->builtin_types.entry_invalid;
|
||||
if (type_requires_comptime(fn_type_id.return_type)) {
|
||||
return get_generic_fn_type(g, &fn_type_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3245,6 +3242,13 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) {
|
||||
} else if (tld->id == TldIdFn) {
|
||||
assert(tld->source_node->type == NodeTypeFnProto);
|
||||
is_export = tld->source_node->data.fn_proto.is_export;
|
||||
|
||||
if (!is_export && !tld->source_node->data.fn_proto.is_extern &&
|
||||
tld->source_node->data.fn_proto.fn_def_node == nullptr)
|
||||
{
|
||||
add_node_error(g, tld->source_node, buf_sprintf("non-extern function has no body"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (is_export) {
|
||||
g->resolve_queue.append(tld);
|
||||
@ -5620,8 +5624,6 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *
|
||||
if (type_entry->id == TypeTableEntryIdInt) {
|
||||
const_val->special = ConstValSpecialStatic;
|
||||
eval_min_max_value_int(g, type_entry, &const_val->data.x_bigint, is_max);
|
||||
} else if (type_entry->id == TypeTableEntryIdFloat) {
|
||||
zig_panic("TODO analyze_min_max_value float");
|
||||
} else if (type_entry->id == TypeTableEntryIdBool) {
|
||||
const_val->special = ConstValSpecialStatic;
|
||||
const_val->data.x_bool = is_max;
|
||||
|
||||
@ -2618,6 +2618,9 @@ static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionPtrCast *instruction)
|
||||
{
|
||||
TypeTableEntry *wanted_type = instruction->base.value.type;
|
||||
if (!type_has_bits(wanted_type)) {
|
||||
return nullptr;
|
||||
}
|
||||
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
|
||||
return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, "");
|
||||
}
|
||||
@ -3036,6 +3039,12 @@ static void gen_set_stack_pointer(CodeGen *g, LLVMValueRef aligned_end_addr) {
|
||||
LLVMBuildCall(g->builder, write_register_fn_val, params, 2, "");
|
||||
}
|
||||
|
||||
static void set_call_instr_sret(CodeGen *g, LLVMValueRef call_instr) {
|
||||
unsigned attr_kind_id = LLVMGetEnumAttributeKindForName("sret", 4);
|
||||
LLVMAttributeRef sret_attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), attr_kind_id, 1);
|
||||
LLVMAddCallSiteAttribute(call_instr, 1, sret_attr);
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) {
|
||||
LLVMValueRef fn_val;
|
||||
TypeTableEntry *fn_type;
|
||||
@ -3131,6 +3140,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
|
||||
} else if (!ret_has_bits) {
|
||||
return nullptr;
|
||||
} else if (first_arg_ret) {
|
||||
set_call_instr_sret(g, result);
|
||||
return instruction->tmp_ptr;
|
||||
} else if (handle_is_ptr(src_return_type)) {
|
||||
auto store_instr = LLVMBuildStore(g->builder, result, instruction->tmp_ptr);
|
||||
@ -4573,8 +4583,9 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f
|
||||
args.append(allocator_val);
|
||||
args.append(coro_size);
|
||||
args.append(alignment_val);
|
||||
ZigLLVMBuildCall(g->builder, alloc_fn_val, args.items, args.length,
|
||||
LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, alloc_fn_val, args.items, args.length,
|
||||
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
|
||||
set_call_instr_sret(g, call_instruction);
|
||||
LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, sret_ptr, err_union_err_index, "");
|
||||
LLVMValueRef err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
|
||||
LLVMBuildStore(g->builder, err_val, err_code_ptr);
|
||||
|
||||
310
src/ir.cpp
310
src/ir.cpp
@ -17,8 +17,7 @@
|
||||
#include "util.hpp"
|
||||
|
||||
struct IrExecContext {
|
||||
ConstExprValue *mem_slot_list;
|
||||
size_t mem_slot_count;
|
||||
ZigList<ConstExprValue *> mem_slot_list;
|
||||
};
|
||||
|
||||
struct IrBuilder {
|
||||
@ -60,7 +59,7 @@ enum ConstCastResultId {
|
||||
ConstCastResultIdType,
|
||||
ConstCastResultIdUnresolvedInferredErrSet,
|
||||
ConstCastResultIdAsyncAllocatorType,
|
||||
ConstCastResultIdNullWrapPtr,
|
||||
ConstCastResultIdNullWrapPtr
|
||||
};
|
||||
|
||||
struct ConstCastOnly;
|
||||
@ -155,18 +154,22 @@ static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type
|
||||
ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {
|
||||
assert(get_codegen_ptr_type(const_val->type) != nullptr);
|
||||
assert(const_val->special == ConstValSpecialStatic);
|
||||
ConstExprValue *result;
|
||||
switch (const_val->data.x_ptr.special) {
|
||||
case ConstPtrSpecialInvalid:
|
||||
zig_unreachable();
|
||||
case ConstPtrSpecialRef:
|
||||
return const_val->data.x_ptr.data.ref.pointee;
|
||||
result = const_val->data.x_ptr.data.ref.pointee;
|
||||
break;
|
||||
case ConstPtrSpecialBaseArray:
|
||||
expand_undef_array(g, const_val->data.x_ptr.data.base_array.array_val);
|
||||
return &const_val->data.x_ptr.data.base_array.array_val->data.x_array.s_none.elements[
|
||||
result = &const_val->data.x_ptr.data.base_array.array_val->data.x_array.s_none.elements[
|
||||
const_val->data.x_ptr.data.base_array.elem_index];
|
||||
break;
|
||||
case ConstPtrSpecialBaseStruct:
|
||||
return &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[
|
||||
result = &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[
|
||||
const_val->data.x_ptr.data.base_struct.field_index];
|
||||
break;
|
||||
case ConstPtrSpecialHardCodedAddr:
|
||||
zig_unreachable();
|
||||
case ConstPtrSpecialDiscard:
|
||||
@ -174,7 +177,8 @@ ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {
|
||||
case ConstPtrSpecialFunction:
|
||||
zig_unreachable();
|
||||
}
|
||||
zig_unreachable();
|
||||
assert(result != nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool ir_should_inline(IrExecutable *exec, Scope *scope) {
|
||||
@ -3181,7 +3185,11 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
|
||||
{
|
||||
IrInstruction *return_value;
|
||||
if (expr_node) {
|
||||
// Temporarily set this so that if we return a type it gets the name of the function
|
||||
FnTableEntry *prev_name_fn = irb->exec->name_fn;
|
||||
irb->exec->name_fn = exec_fn_entry(irb->exec);
|
||||
return_value = ir_gen_node(irb, expr_node, scope);
|
||||
irb->exec->name_fn = prev_name_fn;
|
||||
if (return_value == irb->codegen->invalid_instruction)
|
||||
return irb->codegen->invalid_instruction;
|
||||
} else {
|
||||
@ -3275,7 +3283,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
|
||||
}
|
||||
|
||||
static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope,
|
||||
Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime)
|
||||
Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime,
|
||||
bool skip_name_check)
|
||||
{
|
||||
VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
|
||||
variable_entry->parent_scope = parent_scope;
|
||||
@ -3288,29 +3297,30 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco
|
||||
if (name) {
|
||||
buf_init_from_buf(&variable_entry->name, name);
|
||||
|
||||
VariableTableEntry *existing_var = find_variable(codegen, parent_scope, name);
|
||||
if (existing_var && !existing_var->shadowable) {
|
||||
ErrorMsg *msg = add_node_error(codegen, node,
|
||||
buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
|
||||
add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
|
||||
variable_entry->value->type = codegen->builtin_types.entry_invalid;
|
||||
} else {
|
||||
TypeTableEntry *type = get_primitive_type(codegen, name);
|
||||
if (type != nullptr) {
|
||||
add_node_error(codegen, node,
|
||||
buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
|
||||
if (!skip_name_check) {
|
||||
VariableTableEntry *existing_var = find_variable(codegen, parent_scope, name);
|
||||
if (existing_var && !existing_var->shadowable) {
|
||||
ErrorMsg *msg = add_node_error(codegen, node,
|
||||
buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
|
||||
add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
|
||||
variable_entry->value->type = codegen->builtin_types.entry_invalid;
|
||||
} else {
|
||||
Tld *tld = find_decl(codegen, parent_scope, name);
|
||||
if (tld != nullptr) {
|
||||
ErrorMsg *msg = add_node_error(codegen, node,
|
||||
buf_sprintf("redefinition of '%s'", buf_ptr(name)));
|
||||
add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here"));
|
||||
TypeTableEntry *type = get_primitive_type(codegen, name);
|
||||
if (type != nullptr) {
|
||||
add_node_error(codegen, node,
|
||||
buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
|
||||
variable_entry->value->type = codegen->builtin_types.entry_invalid;
|
||||
} else {
|
||||
Tld *tld = find_decl(codegen, parent_scope, name);
|
||||
if (tld != nullptr) {
|
||||
ErrorMsg *msg = add_node_error(codegen, node,
|
||||
buf_sprintf("redefinition of '%s'", buf_ptr(name)));
|
||||
add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here"));
|
||||
variable_entry->value->type = codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
assert(is_shadowable);
|
||||
// TODO make this name not actually be in scope. user should be able to make a variable called "_anon"
|
||||
@ -3333,14 +3343,9 @@ static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *s
|
||||
bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime)
|
||||
{
|
||||
bool is_underscored = name ? buf_eql_str(name, "_") : false;
|
||||
VariableTableEntry *var = create_local_var( irb->codegen
|
||||
, node
|
||||
, scope
|
||||
, (is_underscored ? nullptr : name)
|
||||
, src_is_const
|
||||
, gen_is_const
|
||||
, (is_underscored ? true : is_shadowable)
|
||||
, is_comptime );
|
||||
VariableTableEntry *var = create_local_var(irb->codegen, node, scope,
|
||||
(is_underscored ? nullptr : name), src_is_const, gen_is_const,
|
||||
(is_underscored ? true : is_shadowable), is_comptime, false);
|
||||
if (is_comptime != nullptr || gen_is_const) {
|
||||
var->mem_slot_index = exec_next_mem_slot(irb->exec);
|
||||
var->owner_exec = irb->exec;
|
||||
@ -6479,20 +6484,17 @@ static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *o
|
||||
static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char *kind_name, AstNode *source_node) {
|
||||
if (exec->name) {
|
||||
return exec->name;
|
||||
} else if (exec->name_fn != nullptr) {
|
||||
Buf *name = buf_alloc();
|
||||
buf_append_buf(name, &exec->name_fn->symbol_name);
|
||||
buf_appendf(name, "(");
|
||||
render_instance_name_recursive(codegen, name, &exec->name_fn->fndef_scope->base, exec->begin_scope);
|
||||
buf_appendf(name, ")");
|
||||
return name;
|
||||
} else {
|
||||
FnTableEntry *fn_entry = exec_fn_entry(exec);
|
||||
if (fn_entry) {
|
||||
Buf *name = buf_alloc();
|
||||
buf_append_buf(name, &fn_entry->symbol_name);
|
||||
buf_appendf(name, "(");
|
||||
render_instance_name_recursive(codegen, name, &fn_entry->fndef_scope->base, exec->begin_scope);
|
||||
buf_appendf(name, ")");
|
||||
return name;
|
||||
} else {
|
||||
//Note: C-imports do not have valid location information
|
||||
return buf_sprintf("(anonymous %s at %s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", kind_name,
|
||||
(source_node->owner->path != nullptr) ? buf_ptr(source_node->owner->path) : "(null)", source_node->line + 1, source_node->column + 1);
|
||||
}
|
||||
//Note: C-imports do not have valid location information
|
||||
return buf_sprintf("(anonymous %s at %s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", kind_name,
|
||||
(source_node->owner->path != nullptr) ? buf_ptr(source_node->owner->path) : "(null)", source_node->line + 1, source_node->column + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6690,7 +6692,10 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
|
||||
return irb->codegen->invalid_instruction;
|
||||
}
|
||||
} else {
|
||||
return_type = nullptr;
|
||||
add_node_error(irb->codegen, node,
|
||||
buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"));
|
||||
return irb->codegen->invalid_instruction;
|
||||
//return_type = nullptr;
|
||||
}
|
||||
|
||||
IrInstruction *async_allocator_type_value = nullptr;
|
||||
@ -8466,9 +8471,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
|
||||
if (wanted_type == actual_type)
|
||||
return result;
|
||||
|
||||
// * and [*] can do a const-cast-only to ?* and ?[*], respectively
|
||||
// but not if there is a mutable parent pointer
|
||||
// and not if the pointer is zero bits
|
||||
// *T and [*]T may const-cast-only to ?*U and ?[*]U, respectively
|
||||
// but not if we want a mutable pointer
|
||||
// and not if the actual pointer has zero bits
|
||||
if (!wanted_is_mutable && wanted_type->id == TypeTableEntryIdOptional &&
|
||||
wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer &&
|
||||
actual_type->id == TypeTableEntryIdPointer && type_has_bits(actual_type))
|
||||
@ -8483,6 +8488,18 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
|
||||
return result;
|
||||
}
|
||||
|
||||
// *T and [*]T can always cast to *c_void
|
||||
if (wanted_type->id == TypeTableEntryIdPointer &&
|
||||
wanted_type->data.pointer.ptr_len == PtrLenSingle &&
|
||||
wanted_type->data.pointer.child_type == g->builtin_types.entry_c_void &&
|
||||
actual_type->id == TypeTableEntryIdPointer &&
|
||||
(!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) &&
|
||||
(!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile))
|
||||
{
|
||||
assert(actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment);
|
||||
return result;
|
||||
}
|
||||
|
||||
// pointer const
|
||||
if (wanted_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer) {
|
||||
ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
|
||||
@ -12478,6 +12495,24 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
|
||||
}
|
||||
}
|
||||
|
||||
if (var->value->type != nullptr && !is_comptime_var) {
|
||||
// This is at least the second time we've seen this variable declaration during analysis.
|
||||
// This means that this is actually a different variable due to, e.g. an inline while loop.
|
||||
// We make a new variable so that it can hold a different type, and so the debug info can
|
||||
// be distinct.
|
||||
VariableTableEntry *new_var = create_local_var(ira->codegen, var->decl_node, var->child_scope,
|
||||
&var->name, var->src_is_const, var->gen_is_const, var->shadowable, var->is_comptime, true);
|
||||
new_var->owner_exec = var->owner_exec;
|
||||
if (var->mem_slot_index != SIZE_MAX) {
|
||||
ConstExprValue *vals = create_const_vals(1);
|
||||
new_var->mem_slot_index = ira->exec_context.mem_slot_list.length;
|
||||
ira->exec_context.mem_slot_list.append(vals);
|
||||
}
|
||||
|
||||
var->next_var = new_var;
|
||||
var = new_var;
|
||||
}
|
||||
|
||||
var->value->type = result_type;
|
||||
assert(var->value->type);
|
||||
|
||||
@ -12496,10 +12531,9 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
|
||||
|
||||
if (casted_init_value->value.special != ConstValSpecialRuntime) {
|
||||
if (var->mem_slot_index != SIZE_MAX) {
|
||||
assert(var->mem_slot_index < ira->exec_context.mem_slot_count);
|
||||
ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
|
||||
copy_const_val(mem_slot, &casted_init_value->value,
|
||||
!is_comptime_var || var->gen_is_const);
|
||||
assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length);
|
||||
ConstExprValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index);
|
||||
copy_const_val(mem_slot, &casted_init_value->value, !is_comptime_var || var->gen_is_const);
|
||||
|
||||
if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) {
|
||||
ir_build_const_from(ira, &decl_var_instruction->base);
|
||||
@ -12960,6 +12994,10 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
|
||||
VariableTableEntry *var)
|
||||
{
|
||||
Error err;
|
||||
while (var->next_var != nullptr) {
|
||||
var = var->next_var;
|
||||
}
|
||||
|
||||
if (var->mem_slot_index != SIZE_MAX && var->owner_exec->analysis == nullptr) {
|
||||
assert(ira->codegen->errors.length != 0);
|
||||
return ira->codegen->invalid_instruction;
|
||||
@ -12979,8 +13017,8 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
|
||||
assert(var->owner_exec != nullptr);
|
||||
assert(var->owner_exec->analysis != nullptr);
|
||||
IrExecContext *exec_context = &var->owner_exec->analysis->exec_context;
|
||||
assert(var->mem_slot_index < exec_context->mem_slot_count);
|
||||
mem_slot = &exec_context->mem_slot_list[var->mem_slot_index];
|
||||
assert(var->mem_slot_index < exec_context->mem_slot_list.length);
|
||||
mem_slot = exec_context->mem_slot_list.at(var->mem_slot_index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -14439,8 +14477,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
|
||||
ConstExprValue *payload_val = union_val->data.x_union.payload;
|
||||
|
||||
TypeTableEntry *field_type = field->type_entry;
|
||||
if (field_type->id == TypeTableEntryIdVoid)
|
||||
{
|
||||
if (field_type->id == TypeTableEntryIdVoid) {
|
||||
assert(payload_val == nullptr);
|
||||
payload_val = create_const_vals(1);
|
||||
payload_val->special = ConstValSpecialStatic;
|
||||
@ -16445,12 +16482,6 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_
|
||||
eval_min_max_value(ira->codegen, target_type, out_val, is_max);
|
||||
return ira->codegen->builtin_types.entry_num_lit_int;
|
||||
}
|
||||
case TypeTableEntryIdFloat:
|
||||
{
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, source_instruction);
|
||||
eval_min_max_value(ira->codegen, target_type, out_val, is_max);
|
||||
return ira->codegen->builtin_types.entry_num_lit_float;
|
||||
}
|
||||
case TypeTableEntryIdBool:
|
||||
case TypeTableEntryIdVoid:
|
||||
{
|
||||
@ -16459,7 +16490,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_
|
||||
return target_type;
|
||||
}
|
||||
case TypeTableEntryIdEnum:
|
||||
zig_panic("TODO min/max value for enum type");
|
||||
case TypeTableEntryIdFloat:
|
||||
case TypeTableEntryIdMetaType:
|
||||
case TypeTableEntryIdUnreachable:
|
||||
case TypeTableEntryIdPointer:
|
||||
@ -16792,12 +16823,11 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na
|
||||
return var->value->data.x_type;
|
||||
}
|
||||
|
||||
static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope)
|
||||
{
|
||||
static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) {
|
||||
Error err;
|
||||
TypeTableEntry *type_info_definition_type = ir_type_info_get_type(ira, "Definition", nullptr);
|
||||
if ((err = ensure_complete_type(ira->codegen, type_info_definition_type)))
|
||||
return false;
|
||||
return err;
|
||||
|
||||
ensure_field_index(type_info_definition_type, "name", 0);
|
||||
ensure_field_index(type_info_definition_type, "is_pub", 1);
|
||||
@ -16805,38 +16835,33 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
|
||||
|
||||
TypeTableEntry *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type);
|
||||
if ((err = ensure_complete_type(ira->codegen, type_info_definition_data_type)))
|
||||
return false;
|
||||
return err;
|
||||
|
||||
TypeTableEntry *type_info_fn_def_type = ir_type_info_get_type(ira, "FnDef", type_info_definition_data_type);
|
||||
if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_type)))
|
||||
return false;
|
||||
return err;
|
||||
|
||||
TypeTableEntry *type_info_fn_def_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_def_type);
|
||||
if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_inline_type)))
|
||||
return false;
|
||||
return err;
|
||||
|
||||
// Loop through our definitions once to figure out how many definitions we will generate info for.
|
||||
auto decl_it = decls_scope->decl_table.entry_iterator();
|
||||
decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr;
|
||||
int definition_count = 0;
|
||||
|
||||
while ((curr_entry = decl_it.next()) != nullptr)
|
||||
{
|
||||
while ((curr_entry = decl_it.next()) != nullptr) {
|
||||
// If the definition is unresolved, force it to be resolved again.
|
||||
if (curr_entry->value->resolution == TldResolutionUnresolved)
|
||||
{
|
||||
if (curr_entry->value->resolution == TldResolutionUnresolved) {
|
||||
resolve_top_level_decl(ira->codegen, curr_entry->value, false, curr_entry->value->source_node);
|
||||
if (curr_entry->value->resolution != TldResolutionOk)
|
||||
{
|
||||
return false;
|
||||
if (curr_entry->value->resolution != TldResolutionOk) {
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip comptime blocks and test functions.
|
||||
if (curr_entry->value->id != TldIdCompTime)
|
||||
{
|
||||
if (curr_entry->value->id == TldIdFn)
|
||||
{
|
||||
if (curr_entry->value->id != TldIdCompTime) {
|
||||
if (curr_entry->value->id == TldIdFn) {
|
||||
FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry;
|
||||
if (fn_entry->is_test)
|
||||
continue;
|
||||
@ -16858,13 +16883,11 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
|
||||
decl_it = decls_scope->decl_table.entry_iterator();
|
||||
curr_entry = nullptr;
|
||||
int definition_index = 0;
|
||||
while ((curr_entry = decl_it.next()) != nullptr)
|
||||
{
|
||||
while ((curr_entry = decl_it.next()) != nullptr) {
|
||||
// Skip comptime blocks and test functions.
|
||||
if (curr_entry->value->id == TldIdCompTime)
|
||||
if (curr_entry->value->id == TldIdCompTime) {
|
||||
continue;
|
||||
else if (curr_entry->value->id == TldIdFn)
|
||||
{
|
||||
} else if (curr_entry->value->id == TldIdFn) {
|
||||
FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry;
|
||||
if (fn_entry->is_test)
|
||||
continue;
|
||||
@ -16887,13 +16910,12 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
|
||||
inner_fields[2].data.x_union.parent.data.p_struct.struct_val = definition_val;
|
||||
inner_fields[2].data.x_union.parent.data.p_struct.field_index = 1;
|
||||
|
||||
switch (curr_entry->value->id)
|
||||
{
|
||||
switch (curr_entry->value->id) {
|
||||
case TldIdVar:
|
||||
{
|
||||
VariableTableEntry *var = ((TldVar *)curr_entry->value)->var;
|
||||
if ((err = ensure_complete_type(ira->codegen, var->value->type)))
|
||||
return false;
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
|
||||
if (var->value->type->id == TypeTableEntryIdMetaType)
|
||||
{
|
||||
@ -17024,7 +17046,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
|
||||
{
|
||||
TypeTableEntry *type_entry = ((TldContainer *)curr_entry->value)->type_entry;
|
||||
if ((err = ensure_complete_type(ira->codegen, type_entry)))
|
||||
return false;
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
|
||||
// This is a type.
|
||||
bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0);
|
||||
@ -17046,7 +17068,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
|
||||
}
|
||||
|
||||
assert(definition_index == definition_count);
|
||||
return true;
|
||||
return ErrorNone;
|
||||
}
|
||||
|
||||
static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, TypeTableEntry *ptr_type_entry) {
|
||||
@ -17104,30 +17126,31 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, TypeTableEntry
|
||||
return result;
|
||||
};
|
||||
|
||||
static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) {
|
||||
static void make_enum_field_val(IrAnalyze *ira, ConstExprValue *enum_field_val, TypeEnumField *enum_field,
|
||||
TypeTableEntry *type_info_enum_field_type)
|
||||
{
|
||||
enum_field_val->special = ConstValSpecialStatic;
|
||||
enum_field_val->type = type_info_enum_field_type;
|
||||
|
||||
ConstExprValue *inner_fields = create_const_vals(2);
|
||||
inner_fields[1].special = ConstValSpecialStatic;
|
||||
inner_fields[1].type = ira->codegen->builtin_types.entry_usize;
|
||||
|
||||
ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name);
|
||||
init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true);
|
||||
|
||||
bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value);
|
||||
|
||||
enum_field_val->data.x_struct.fields = inner_fields;
|
||||
}
|
||||
|
||||
static Error ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry, ConstExprValue **out) {
|
||||
Error err;
|
||||
assert(type_entry != nullptr);
|
||||
assert(!type_is_invalid(type_entry));
|
||||
|
||||
if ((err = ensure_complete_type(ira->codegen, type_entry)))
|
||||
return nullptr;
|
||||
|
||||
const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field,
|
||||
TypeTableEntry *type_info_enum_field_type) {
|
||||
enum_field_val->special = ConstValSpecialStatic;
|
||||
enum_field_val->type = type_info_enum_field_type;
|
||||
|
||||
ConstExprValue *inner_fields = create_const_vals(2);
|
||||
inner_fields[1].special = ConstValSpecialStatic;
|
||||
inner_fields[1].type = ira->codegen->builtin_types.entry_usize;
|
||||
|
||||
ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name);
|
||||
init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true);
|
||||
|
||||
bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value);
|
||||
|
||||
enum_field_val->data.x_struct.fields = inner_fields;
|
||||
};
|
||||
return err;
|
||||
|
||||
if (type_entry == ira->codegen->builtin_types.entry_global_error_set) {
|
||||
zig_panic("TODO implement @typeInfo for global error set");
|
||||
@ -17150,13 +17173,16 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
|
||||
case TypeTableEntryIdBlock:
|
||||
case TypeTableEntryIdArgTuple:
|
||||
case TypeTableEntryIdOpaque:
|
||||
return nullptr;
|
||||
*out = nullptr;
|
||||
return ErrorNone;
|
||||
default:
|
||||
{
|
||||
// Lookup an available value in our cache.
|
||||
auto entry = ira->codegen->type_info_cache.maybe_get(type_entry);
|
||||
if (entry != nullptr)
|
||||
return entry->value;
|
||||
if (entry != nullptr) {
|
||||
*out = entry->value;
|
||||
return ErrorNone;
|
||||
}
|
||||
|
||||
// Fallthrough if we don't find one.
|
||||
}
|
||||
@ -17307,15 +17333,15 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
|
||||
{
|
||||
TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index];
|
||||
ConstExprValue *enum_field_val = &enum_field_array->data.x_array.s_none.elements[enum_field_index];
|
||||
make_enum_field_val(enum_field_val, enum_field, type_info_enum_field_type);
|
||||
make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type);
|
||||
enum_field_val->data.x_struct.parent.id = ConstParentIdArray;
|
||||
enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array;
|
||||
enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index;
|
||||
}
|
||||
// defs: []TypeInfo.Definition
|
||||
ensure_field_index(result->type, "defs", 3);
|
||||
if (!ir_make_type_info_defs(ira, &fields[3], type_entry->data.enumeration.decls_scope))
|
||||
return nullptr;
|
||||
if ((err = ir_make_type_info_defs(ira, &fields[3], type_entry->data.enumeration.decls_scope)))
|
||||
return err;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -17341,8 +17367,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
|
||||
error_array->data.x_array.s_none.elements = create_const_vals(error_count);
|
||||
|
||||
init_const_slice(ira->codegen, &fields[0], error_array, 0, error_count, false);
|
||||
for (uint32_t error_index = 0; error_index < error_count; error_index++)
|
||||
{
|
||||
for (uint32_t error_index = 0; error_index < error_count; error_index++) {
|
||||
ErrorTableEntry *error = type_entry->data.error_set.errors[error_index];
|
||||
ConstExprValue *error_val = &error_array->data.x_array.s_none.elements[error_index];
|
||||
|
||||
@ -17420,9 +17445,9 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
|
||||
tag_type->type = ira->codegen->builtin_types.entry_type;
|
||||
tag_type->data.x_type = type_entry->data.unionation.tag_type;
|
||||
fields[1].data.x_optional = tag_type;
|
||||
}
|
||||
else
|
||||
} else {
|
||||
fields[1].data.x_optional = nullptr;
|
||||
}
|
||||
// fields: []TypeInfo.UnionField
|
||||
ensure_field_index(result->type, "fields", 2);
|
||||
|
||||
@ -17455,7 +17480,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
|
||||
inner_fields[1].data.x_optional = nullptr;
|
||||
} else {
|
||||
inner_fields[1].data.x_optional = create_const_vals(1);
|
||||
make_enum_field_val(inner_fields[1].data.x_optional, union_field->enum_field, type_info_enum_field_type);
|
||||
make_enum_field_val(ira, inner_fields[1].data.x_optional, union_field->enum_field, type_info_enum_field_type);
|
||||
}
|
||||
|
||||
inner_fields[2].special = ConstValSpecialStatic;
|
||||
@ -17472,8 +17497,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
|
||||
}
|
||||
// defs: []TypeInfo.Definition
|
||||
ensure_field_index(result->type, "defs", 3);
|
||||
if (!ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope))
|
||||
return nullptr;
|
||||
if ((err = ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope)))
|
||||
return err;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -17546,8 +17571,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
|
||||
}
|
||||
// defs: []TypeInfo.Definition
|
||||
ensure_field_index(result->type, "defs", 2);
|
||||
if (!ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope))
|
||||
return nullptr;
|
||||
if ((err = ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope)))
|
||||
return err;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -17660,7 +17685,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
|
||||
{
|
||||
TypeTableEntry *fn_type = type_entry->data.bound_fn.fn_type;
|
||||
assert(fn_type->id == TypeTableEntryIdFn);
|
||||
result = ir_make_type_info_value(ira, fn_type);
|
||||
if ((err = ir_make_type_info_value(ira, fn_type, &result)))
|
||||
return err;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -17668,12 +17694,14 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
|
||||
|
||||
assert(result != nullptr);
|
||||
ira->codegen->type_info_cache.put(type_entry, result);
|
||||
return result;
|
||||
*out = result;
|
||||
return ErrorNone;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira,
|
||||
IrInstructionTypeInfo *instruction)
|
||||
{
|
||||
Error err;
|
||||
IrInstruction *type_value = instruction->type_value->other;
|
||||
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
|
||||
if (type_is_invalid(type_entry))
|
||||
@ -17681,15 +17709,16 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira,
|
||||
|
||||
TypeTableEntry *result_type = ir_type_info_get_type(ira, nullptr, nullptr);
|
||||
|
||||
ConstExprValue *payload;
|
||||
if ((err = ir_make_type_info_value(ira, type_entry, &payload)))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
|
||||
out_val->type = result_type;
|
||||
bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry));
|
||||
|
||||
ConstExprValue *payload = ir_make_type_info_value(ira, type_entry);
|
||||
out_val->data.x_union.payload = payload;
|
||||
|
||||
if (payload != nullptr)
|
||||
{
|
||||
if (payload != nullptr) {
|
||||
assert(payload->type->id == TypeTableEntryIdStruct);
|
||||
payload->data.x_struct.parent.id = ConstParentIdUnion;
|
||||
payload->data.x_struct.parent.data.p_union.union_val = out_val;
|
||||
@ -19731,6 +19760,8 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) {
|
||||
Error err;
|
||||
|
||||
IrInstruction *dest_type_value = instruction->dest_type->other;
|
||||
TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);
|
||||
if (type_is_invalid(dest_type))
|
||||
@ -19784,9 +19815,13 @@ static TypeTableEntry *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruc
|
||||
instruction->base.source_node, nullptr, ptr);
|
||||
casted_ptr->value.type = dest_type;
|
||||
|
||||
// keep the bigger alignment, it can only help
|
||||
// Keep the bigger alignment, it can only help-
|
||||
// unless the target is zero bits.
|
||||
if ((err = type_ensure_zero_bits_known(ira->codegen, dest_type)))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *result;
|
||||
if (src_align_bytes > dest_align_bytes) {
|
||||
if (src_align_bytes > dest_align_bytes && type_has_bits(dest_type)) {
|
||||
result = ir_align_cast(ira, casted_ptr, src_align_bytes, false);
|
||||
if (type_is_invalid(result->value.type))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
@ -21192,8 +21227,11 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl
|
||||
ira->new_irb.codegen = codegen;
|
||||
ira->new_irb.exec = new_exec;
|
||||
|
||||
ira->exec_context.mem_slot_count = ira->old_irb.exec->mem_slot_count;
|
||||
ira->exec_context.mem_slot_list = create_const_vals(ira->exec_context.mem_slot_count);
|
||||
ConstExprValue *vals = create_const_vals(ira->old_irb.exec->mem_slot_count);
|
||||
ira->exec_context.mem_slot_list.resize(ira->old_irb.exec->mem_slot_count);
|
||||
for (size_t i = 0; i < ira->exec_context.mem_slot_list.length; i += 1) {
|
||||
ira->exec_context.mem_slot_list.items[i] = &vals[i];
|
||||
}
|
||||
|
||||
IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0);
|
||||
IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb, nullptr);
|
||||
|
||||
@ -3075,12 +3075,19 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
|
||||
trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt));
|
||||
case Stmt::DeclStmtClass:
|
||||
return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope);
|
||||
case Stmt::DoStmtClass:
|
||||
case Stmt::WhileStmtClass: {
|
||||
AstNode *while_node = trans_while_loop(c, scope, (const WhileStmt *)stmt);
|
||||
AstNode *while_node = sc == Stmt::DoStmtClass
|
||||
? trans_do_loop(c, scope, (const DoStmt *)stmt)
|
||||
: trans_while_loop(c, scope, (const WhileStmt *)stmt);
|
||||
|
||||
if (while_node == nullptr)
|
||||
return ErrorUnexpected;
|
||||
|
||||
assert(while_node->type == NodeTypeWhileExpr);
|
||||
if (while_node->data.while_expr.body == nullptr) {
|
||||
if (while_node->data.while_expr.body == nullptr)
|
||||
while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock);
|
||||
}
|
||||
|
||||
return wrap_stmt(out_node, out_child_scope, scope, while_node);
|
||||
}
|
||||
case Stmt::IfStmtClass:
|
||||
@ -3105,14 +3112,6 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
|
||||
case Stmt::UnaryExprOrTypeTraitExprClass:
|
||||
return wrap_stmt(out_node, out_child_scope, scope,
|
||||
trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt));
|
||||
case Stmt::DoStmtClass: {
|
||||
AstNode *while_node = trans_do_loop(c, scope, (const DoStmt *)stmt);
|
||||
assert(while_node->type == NodeTypeWhileExpr);
|
||||
if (while_node->data.while_expr.body == nullptr) {
|
||||
while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock);
|
||||
}
|
||||
return wrap_stmt(out_node, out_child_scope, scope, while_node);
|
||||
}
|
||||
case Stmt::ForStmtClass: {
|
||||
AstNode *node = trans_for_loop(c, scope, (const ForStmt *)stmt);
|
||||
return wrap_stmt(out_node, out_child_scope, scope, node);
|
||||
|
||||
@ -204,7 +204,11 @@ static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) {
|
||||
// https://developer.microsoft.com/en-us/windows/downloads/sdk-archive
|
||||
c2 = 26624;
|
||||
}
|
||||
if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) {
|
||||
|
||||
if ( (c0 > v0)
|
||||
|| (c0 == v0 && c1 > v1)
|
||||
|| (c0 == v0 && c1 == v1 && c2 > v2)
|
||||
|| (c0 == v0 && c1 == v1 && c2 == v2 && c3 > v3) ) {
|
||||
v0 = c0, v1 = c1, v2 = c2, v3 = c3;
|
||||
free((void*)priv->base.version10_ptr);
|
||||
priv->base.version10_ptr = strdup(ffd.cFileName);
|
||||
@ -244,7 +248,8 @@ static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) {
|
||||
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
int c0 = 0, c1 = 0;
|
||||
sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
|
||||
if ((c0 > v0) || (c1 > v1)) {
|
||||
|
||||
if ( (c0 > v0) || (c0 == v0 && c1 > v1) ) {
|
||||
v0 = c0, v1 = c1;
|
||||
free((void*)priv->base.version81_ptr);
|
||||
priv->base.version81_ptr = strdup(ffd.cFileName);
|
||||
|
||||
230
std/coff.zig
Normal file
230
std/coff.zig
Normal file
@ -0,0 +1,230 @@
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("index.zig");
|
||||
const io = std.io;
|
||||
const mem = std.mem;
|
||||
const os = std.os;
|
||||
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
// CoffHeader.machine values
|
||||
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx
|
||||
const IMAGE_FILE_MACHINE_I386 = 0x014c;
|
||||
const IMAGE_FILE_MACHINE_IA64 = 0x0200;
|
||||
const IMAGE_FILE_MACHINE_AMD64 = 0x8664;
|
||||
|
||||
// OptionalHeader.magic values
|
||||
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
|
||||
const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
|
||||
const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
|
||||
|
||||
const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
|
||||
const DEBUG_DIRECTORY = 6;
|
||||
|
||||
pub const CoffError = error {
|
||||
InvalidPEMagic,
|
||||
InvalidPEHeader,
|
||||
InvalidMachine,
|
||||
MissingCoffSection,
|
||||
};
|
||||
|
||||
pub const Coff = struct {
|
||||
in_file: os.File,
|
||||
allocator: *mem.Allocator,
|
||||
|
||||
coff_header: CoffHeader,
|
||||
pe_header: OptionalHeader,
|
||||
sections: ArrayList(Section),
|
||||
|
||||
guid: [16]u8,
|
||||
age: u32,
|
||||
|
||||
pub fn loadHeader(self: *Coff) !void {
|
||||
const pe_pointer_offset = 0x3C;
|
||||
|
||||
var file_stream = io.FileInStream.init(self.in_file);
|
||||
const in = &file_stream.stream;
|
||||
|
||||
var magic: [2]u8 = undefined;
|
||||
try in.readNoEof(magic[0..]);
|
||||
if (!mem.eql(u8, magic, "MZ"))
|
||||
return error.InvalidPEMagic;
|
||||
|
||||
// Seek to PE File Header (coff header)
|
||||
try self.in_file.seekTo(pe_pointer_offset);
|
||||
const pe_magic_offset = try in.readIntLe(u32);
|
||||
try self.in_file.seekTo(pe_magic_offset);
|
||||
|
||||
var pe_header_magic: [4]u8 = undefined;
|
||||
try in.readNoEof(pe_header_magic[0..]);
|
||||
if (!mem.eql(u8, pe_header_magic, []u8{'P', 'E', 0, 0}))
|
||||
return error.InvalidPEHeader;
|
||||
|
||||
self.coff_header = CoffHeader {
|
||||
.machine = try in.readIntLe(u16),
|
||||
.number_of_sections = try in.readIntLe(u16),
|
||||
.timedate_stamp = try in.readIntLe(u32),
|
||||
.pointer_to_symbol_table = try in.readIntLe(u32),
|
||||
.number_of_symbols = try in.readIntLe(u32),
|
||||
.size_of_optional_header = try in.readIntLe(u16),
|
||||
.characteristics = try in.readIntLe(u16),
|
||||
};
|
||||
|
||||
switch (self.coff_header.machine) {
|
||||
IMAGE_FILE_MACHINE_I386,
|
||||
IMAGE_FILE_MACHINE_AMD64,
|
||||
IMAGE_FILE_MACHINE_IA64
|
||||
=> {},
|
||||
else => return error.InvalidMachine,
|
||||
}
|
||||
|
||||
try self.loadOptionalHeader(&file_stream);
|
||||
}
|
||||
|
||||
fn loadOptionalHeader(self: *Coff, file_stream: *io.FileInStream) !void {
|
||||
const in = &file_stream.stream;
|
||||
self.pe_header.magic = try in.readIntLe(u16);
|
||||
// For now we're only interested in finding the reference to the .pdb,
|
||||
// so we'll skip most of this header, which size is different in 32
|
||||
// 64 bits by the way.
|
||||
var skip_size: u16 = undefined;
|
||||
if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
||||
skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 18 * @sizeOf(u32);
|
||||
}
|
||||
else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
||||
skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 12 * @sizeOf(u32) + 5 * @sizeOf(u64);
|
||||
}
|
||||
else
|
||||
return error.InvalidPEMagic;
|
||||
|
||||
try self.in_file.seekForward(skip_size);
|
||||
|
||||
const number_of_rva_and_sizes = try in.readIntLe(u32);
|
||||
if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
|
||||
return error.InvalidPEHeader;
|
||||
|
||||
for (self.pe_header.data_directory) |*data_dir| {
|
||||
data_dir.* = OptionalHeader.DataDirectory {
|
||||
.virtual_address = try in.readIntLe(u32),
|
||||
.size = try in.readIntLe(u32),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getPdbPath(self: *Coff, buffer: []u8) !usize {
|
||||
try self.loadSections();
|
||||
const header = (self.getSection(".rdata") orelse return error.MissingCoffSection).header;
|
||||
|
||||
// The linker puts a chunk that contains the .pdb path right after the
|
||||
// debug_directory.
|
||||
const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];
|
||||
const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
|
||||
try self.in_file.seekTo(file_offset + debug_dir.size);
|
||||
|
||||
var file_stream = io.FileInStream.init(self.in_file);
|
||||
const in = &file_stream.stream;
|
||||
|
||||
var cv_signature: [4]u8 = undefined; // CodeView signature
|
||||
try in.readNoEof(cv_signature[0..]);
|
||||
// 'RSDS' indicates PDB70 format, used by lld.
|
||||
if (!mem.eql(u8, cv_signature, "RSDS"))
|
||||
return error.InvalidPEMagic;
|
||||
try in.readNoEof(self.guid[0..]);
|
||||
self.age = try in.readIntLe(u32);
|
||||
|
||||
// Finally read the null-terminated string.
|
||||
var byte = try in.readByte();
|
||||
var i: usize = 0;
|
||||
while (byte != 0 and i < buffer.len) : (i += 1) {
|
||||
buffer[i] = byte;
|
||||
byte = try in.readByte();
|
||||
}
|
||||
|
||||
if (byte != 0 and i == buffer.len)
|
||||
return error.NameTooLong;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
pub fn loadSections(self: *Coff) !void {
|
||||
if (self.sections.len != 0)
|
||||
return;
|
||||
|
||||
self.sections = ArrayList(Section).init(self.allocator);
|
||||
|
||||
var file_stream = io.FileInStream.init(self.in_file);
|
||||
const in = &file_stream.stream;
|
||||
|
||||
var name: [8]u8 = undefined;
|
||||
|
||||
var i: u16 = 0;
|
||||
while (i < self.coff_header.number_of_sections) : (i += 1) {
|
||||
try in.readNoEof(name[0..]);
|
||||
try self.sections.append(Section {
|
||||
.header = SectionHeader {
|
||||
.name = name,
|
||||
.misc = SectionHeader.Misc { .physical_address = try in.readIntLe(u32) },
|
||||
.virtual_address = try in.readIntLe(u32),
|
||||
.size_of_raw_data = try in.readIntLe(u32),
|
||||
.pointer_to_raw_data = try in.readIntLe(u32),
|
||||
.pointer_to_relocations = try in.readIntLe(u32),
|
||||
.pointer_to_line_numbers = try in.readIntLe(u32),
|
||||
.number_of_relocations = try in.readIntLe(u16),
|
||||
.number_of_line_numbers = try in.readIntLe(u16),
|
||||
.characteristics = try in.readIntLe(u32),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getSection(self: *Coff, comptime name: []const u8) ?*Section {
|
||||
for (self.sections.toSlice()) |*sec| {
|
||||
if (mem.eql(u8, sec.header.name[0..name.len], name)) {
|
||||
return sec;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const CoffHeader = struct {
|
||||
machine: u16,
|
||||
number_of_sections: u16,
|
||||
timedate_stamp: u32,
|
||||
pointer_to_symbol_table: u32,
|
||||
number_of_symbols: u32,
|
||||
size_of_optional_header: u16,
|
||||
characteristics: u16
|
||||
};
|
||||
|
||||
const OptionalHeader = struct {
|
||||
const DataDirectory = struct {
|
||||
virtual_address: u32,
|
||||
size: u32
|
||||
};
|
||||
|
||||
magic: u16,
|
||||
data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory,
|
||||
};
|
||||
|
||||
pub const Section = struct {
|
||||
header: SectionHeader,
|
||||
};
|
||||
|
||||
const SectionHeader = struct {
|
||||
const Misc = union {
|
||||
physical_address: u32,
|
||||
virtual_size: u32
|
||||
};
|
||||
|
||||
name: [8]u8,
|
||||
misc: Misc,
|
||||
virtual_address: u32,
|
||||
size_of_raw_data: u32,
|
||||
pointer_to_raw_data: u32,
|
||||
pointer_to_relocations: u32,
|
||||
pointer_to_line_numbers: u32,
|
||||
number_of_relocations: u16,
|
||||
number_of_line_numbers: u16,
|
||||
characteristics: u32,
|
||||
};
|
||||
@ -34,8 +34,8 @@ pub const Blake2s256 = Blake2s(256);
|
||||
fn Blake2s(comptime out_len: usize) type {
|
||||
return struct {
|
||||
const Self = this;
|
||||
const block_size = 64;
|
||||
const digest_size = out_len / 8;
|
||||
const block_length = 64;
|
||||
const digest_length = out_len / 8;
|
||||
|
||||
const iv = [8]u32{
|
||||
0x6A09E667,
|
||||
@ -250,8 +250,8 @@ test "blake2s256 streaming" {
|
||||
}
|
||||
|
||||
test "blake2s256 aligned final" {
|
||||
var block = []u8{0} ** Blake2s256.block_size;
|
||||
var out: [Blake2s256.digest_size]u8 = undefined;
|
||||
var block = []u8{0} ** Blake2s256.block_length;
|
||||
var out: [Blake2s256.digest_length]u8 = undefined;
|
||||
|
||||
var h = Blake2s256.init();
|
||||
h.update(block);
|
||||
@ -267,8 +267,8 @@ pub const Blake2b512 = Blake2b(512);
|
||||
fn Blake2b(comptime out_len: usize) type {
|
||||
return struct {
|
||||
const Self = this;
|
||||
const block_size = 128;
|
||||
const digest_size = out_len / 8;
|
||||
const block_length = 128;
|
||||
const digest_length = out_len / 8;
|
||||
|
||||
const iv = [8]u64{
|
||||
0x6a09e667f3bcc908,
|
||||
@ -483,8 +483,8 @@ test "blake2b512 streaming" {
|
||||
}
|
||||
|
||||
test "blake2b512 aligned final" {
|
||||
var block = []u8{0} ** Blake2b512.block_size;
|
||||
var out: [Blake2b512.digest_size]u8 = undefined;
|
||||
var block = []u8{0} ** Blake2b512.block_length;
|
||||
var out: [Blake2b512.digest_length]u8 = undefined;
|
||||
|
||||
var h = Blake2b512.init();
|
||||
h.update(block);
|
||||
|
||||
432
std/crypto/chacha20.zig
Normal file
432
std/crypto/chacha20.zig
Normal file
@ -0,0 +1,432 @@
|
||||
// Based on public domain Supercop by Daniel J. Bernstein
|
||||
|
||||
const std = @import("../index.zig");
|
||||
const mem = std.mem;
|
||||
const endian = std.endian;
|
||||
const assert = std.debug.assert;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const QuarterRound = struct {
|
||||
a: usize,
|
||||
b: usize,
|
||||
c: usize,
|
||||
d: usize,
|
||||
};
|
||||
|
||||
fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound {
|
||||
return QuarterRound{
|
||||
.a = a,
|
||||
.b = b,
|
||||
.c = c,
|
||||
.d = d,
|
||||
};
|
||||
}
|
||||
|
||||
// The chacha family of ciphers are based on the salsa family.
|
||||
fn salsa20_wordtobyte(out: []u8, input: [16]u32) void {
|
||||
assert(out.len >= 64);
|
||||
|
||||
var x: [16]u32 = undefined;
|
||||
|
||||
for (x) |_, i|
|
||||
x[i] = input[i];
|
||||
|
||||
const rounds = comptime []QuarterRound{
|
||||
Rp(0, 4, 8, 12),
|
||||
Rp(1, 5, 9, 13),
|
||||
Rp(2, 6, 10, 14),
|
||||
Rp(3, 7, 11, 15),
|
||||
Rp(0, 5, 10, 15),
|
||||
Rp(1, 6, 11, 12),
|
||||
Rp(2, 7, 8, 13),
|
||||
Rp(3, 4, 9, 14),
|
||||
};
|
||||
|
||||
comptime var j: usize = 0;
|
||||
inline while (j < 20) : (j += 2) {
|
||||
// two-round cycles
|
||||
inline for (rounds) |r| {
|
||||
x[r.a] +%= x[r.b];
|
||||
x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], u32(16));
|
||||
x[r.c] +%= x[r.d];
|
||||
x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], u32(12));
|
||||
x[r.a] +%= x[r.b];
|
||||
x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], u32(8));
|
||||
x[r.c] +%= x[r.d];
|
||||
x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], u32(7));
|
||||
}
|
||||
}
|
||||
|
||||
for (x) |_, i| {
|
||||
mem.writeInt(out[4 * i .. 4 * i + 4], x[i] +% input[i], builtin.Endian.Little);
|
||||
}
|
||||
}
|
||||
|
||||
fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {
|
||||
var ctx: [16]u32 = undefined;
|
||||
var remaining: usize = if (in.len > out.len) in.len else out.len;
|
||||
var cursor: usize = 0;
|
||||
|
||||
const c = "expand 32-byte k";
|
||||
const constant_le = []u32{
|
||||
mem.readIntLE(u32, c[0..4]),
|
||||
mem.readIntLE(u32, c[4..8]),
|
||||
mem.readIntLE(u32, c[8..12]),
|
||||
mem.readIntLE(u32, c[12..16]),
|
||||
};
|
||||
|
||||
mem.copy(u32, ctx[0..], constant_le[0..4]);
|
||||
mem.copy(u32, ctx[4..12], key[0..8]);
|
||||
mem.copy(u32, ctx[12..16], counter[0..4]);
|
||||
|
||||
while (true) {
|
||||
var buf: [64]u8 = undefined;
|
||||
salsa20_wordtobyte(buf[0..], ctx);
|
||||
|
||||
if (remaining < 64) {
|
||||
var i: usize = 0;
|
||||
while (i < remaining) : (i += 1)
|
||||
out[cursor + i] = in[cursor + i] ^ buf[i];
|
||||
return;
|
||||
}
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < 64) : (i += 1)
|
||||
out[cursor + i] = in[cursor + i] ^ buf[i];
|
||||
|
||||
cursor += 64;
|
||||
remaining -= 64;
|
||||
|
||||
ctx[12] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// ChaCha20 avoids the possibility of timing attacks, as there are no branches
|
||||
/// on secret key data.
|
||||
///
|
||||
/// in and out should be the same length.
|
||||
/// counter should generally be 0 or 1
|
||||
///
|
||||
/// ChaCha20 is self-reversing. To decrypt just run the cipher with the same
|
||||
/// counter, nonce, and key.
|
||||
pub fn chaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [12]u8) void {
|
||||
assert(in.len >= out.len);
|
||||
assert((in.len >> 6) + counter <= @maxValue(u32));
|
||||
|
||||
var k: [8]u32 = undefined;
|
||||
var c: [4]u32 = undefined;
|
||||
|
||||
k[0] = mem.readIntLE(u32, key[0..4]);
|
||||
k[1] = mem.readIntLE(u32, key[4..8]);
|
||||
k[2] = mem.readIntLE(u32, key[8..12]);
|
||||
k[3] = mem.readIntLE(u32, key[12..16]);
|
||||
k[4] = mem.readIntLE(u32, key[16..20]);
|
||||
k[5] = mem.readIntLE(u32, key[20..24]);
|
||||
k[6] = mem.readIntLE(u32, key[24..28]);
|
||||
k[7] = mem.readIntLE(u32, key[28..32]);
|
||||
|
||||
c[0] = counter;
|
||||
c[1] = mem.readIntLE(u32, nonce[0..4]);
|
||||
c[2] = mem.readIntLE(u32, nonce[4..8]);
|
||||
c[3] = mem.readIntLE(u32, nonce[8..12]);
|
||||
chaCha20_internal(out, in, k, c);
|
||||
}
|
||||
|
||||
/// This is the original ChaCha20 before RFC 7539, which recommends using the
|
||||
/// orgininal version on applications such as disk or file encryption that might
|
||||
/// exceed the 256 GiB limit of the 96-bit nonce version.
|
||||
pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]u8, nonce: [8]u8) void {
|
||||
assert(in.len >= out.len);
|
||||
assert(counter +% (in.len >> 6) >= counter);
|
||||
|
||||
var cursor: u64 = 0;
|
||||
var k: [8]u32 = undefined;
|
||||
var c: [4]u32 = undefined;
|
||||
|
||||
k[0] = mem.readIntLE(u32, key[0..4]);
|
||||
k[1] = mem.readIntLE(u32, key[4..8]);
|
||||
k[2] = mem.readIntLE(u32, key[8..12]);
|
||||
k[3] = mem.readIntLE(u32, key[12..16]);
|
||||
k[4] = mem.readIntLE(u32, key[16..20]);
|
||||
k[5] = mem.readIntLE(u32, key[20..24]);
|
||||
k[6] = mem.readIntLE(u32, key[24..28]);
|
||||
k[7] = mem.readIntLE(u32, key[28..32]);
|
||||
|
||||
c[0] = @truncate(u32, counter);
|
||||
c[1] = @truncate(u32, counter >> 32);
|
||||
c[2] = mem.readIntLE(u32, nonce[0..4]);
|
||||
c[3] = mem.readIntLE(u32, nonce[4..8]);
|
||||
|
||||
const block_size = (1 << 6);
|
||||
const big_block = (block_size << 32);
|
||||
|
||||
// first partial big block
|
||||
if (((@intCast(u64, @maxValue(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) {
|
||||
chaCha20_internal(out[cursor..big_block], in[cursor..big_block], k, c);
|
||||
cursor = big_block - cursor;
|
||||
c[1] += 1;
|
||||
if (comptime @sizeOf(usize) > 4) {
|
||||
// A big block is giant: 256 GiB, but we can avoid this limitation
|
||||
var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));
|
||||
var i: u32 = 0;
|
||||
while (remaining_blocks > 0) : (remaining_blocks -= 1) {
|
||||
chaCha20_internal(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c);
|
||||
c[1] += 1; // upper 32-bit of counter, generic chaCha20_internal() doesn't know about this.
|
||||
cursor += big_block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chaCha20_internal(out[cursor..], in[cursor..], k, c);
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc7539#section-2.4.2
|
||||
test "crypto.chacha20 test vector sunscreen" {
|
||||
const expected_result = []u8{
|
||||
0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80,
|
||||
0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81,
|
||||
0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2,
|
||||
0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b,
|
||||
0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab,
|
||||
0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57,
|
||||
0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab,
|
||||
0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8,
|
||||
0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61,
|
||||
0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e,
|
||||
0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06,
|
||||
0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36,
|
||||
0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6,
|
||||
0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
|
||||
0x87, 0x4d,
|
||||
};
|
||||
const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
|
||||
var result: [114]u8 = undefined;
|
||||
const key = []u8{
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31,
|
||||
};
|
||||
const nonce = []u8{
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0x4a,
|
||||
0, 0, 0, 0,
|
||||
};
|
||||
|
||||
chaCha20IETF(result[0..], input[0..], 1, key, nonce);
|
||||
assert(mem.eql(u8, expected_result, result));
|
||||
|
||||
// Chacha20 is self-reversing.
|
||||
var plaintext: [114]u8 = undefined;
|
||||
chaCha20IETF(plaintext[0..], result[0..], 1, key, nonce);
|
||||
assert(mem.compare(u8, input, plaintext) == mem.Compare.Equal);
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
|
||||
test "crypto.chacha20 test vector 1" {
|
||||
const expected_result = []u8{
|
||||
0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90,
|
||||
0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28,
|
||||
0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a,
|
||||
0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7,
|
||||
0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d,
|
||||
0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37,
|
||||
0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c,
|
||||
0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86,
|
||||
};
|
||||
const input = []u8{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
var result: [64]u8 = undefined;
|
||||
const key = []u8{
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
const nonce = []u8{ 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
|
||||
assert(mem.eql(u8, expected_result, result));
|
||||
}
|
||||
|
||||
test "crypto.chacha20 test vector 2" {
|
||||
const expected_result = []u8{
|
||||
0x45, 0x40, 0xf0, 0x5a, 0x9f, 0x1f, 0xb2, 0x96,
|
||||
0xd7, 0x73, 0x6e, 0x7b, 0x20, 0x8e, 0x3c, 0x96,
|
||||
0xeb, 0x4f, 0xe1, 0x83, 0x46, 0x88, 0xd2, 0x60,
|
||||
0x4f, 0x45, 0x09, 0x52, 0xed, 0x43, 0x2d, 0x41,
|
||||
0xbb, 0xe2, 0xa0, 0xb6, 0xea, 0x75, 0x66, 0xd2,
|
||||
0xa5, 0xd1, 0xe7, 0xe2, 0x0d, 0x42, 0xaf, 0x2c,
|
||||
0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81,
|
||||
0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63,
|
||||
};
|
||||
const input = []u8{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
var result: [64]u8 = undefined;
|
||||
const key = []u8{
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1,
|
||||
};
|
||||
const nonce = []u8{ 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
|
||||
assert(mem.eql(u8, expected_result, result));
|
||||
}
|
||||
|
||||
test "crypto.chacha20 test vector 3" {
|
||||
const expected_result = []u8{
|
||||
0xde, 0x9c, 0xba, 0x7b, 0xf3, 0xd6, 0x9e, 0xf5,
|
||||
0xe7, 0x86, 0xdc, 0x63, 0x97, 0x3f, 0x65, 0x3a,
|
||||
0x0b, 0x49, 0xe0, 0x15, 0xad, 0xbf, 0xf7, 0x13,
|
||||
0x4f, 0xcb, 0x7d, 0xf1, 0x37, 0x82, 0x10, 0x31,
|
||||
0xe8, 0x5a, 0x05, 0x02, 0x78, 0xa7, 0x08, 0x45,
|
||||
0x27, 0x21, 0x4f, 0x73, 0xef, 0xc7, 0xfa, 0x5b,
|
||||
0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e,
|
||||
0x44, 0x5f, 0x41, 0xe3,
|
||||
};
|
||||
const input = []u8{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
var result: [60]u8 = undefined;
|
||||
const key = []u8{
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
const nonce = []u8{ 0, 0, 0, 0, 0, 0, 0, 1 };
|
||||
|
||||
chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
|
||||
assert(mem.eql(u8, expected_result, result));
|
||||
}
|
||||
|
||||
test "crypto.chacha20 test vector 4" {
|
||||
const expected_result = []u8{
|
||||
0xef, 0x3f, 0xdf, 0xd6, 0xc6, 0x15, 0x78, 0xfb,
|
||||
0xf5, 0xcf, 0x35, 0xbd, 0x3d, 0xd3, 0x3b, 0x80,
|
||||
0x09, 0x63, 0x16, 0x34, 0xd2, 0x1e, 0x42, 0xac,
|
||||
0x33, 0x96, 0x0b, 0xd1, 0x38, 0xe5, 0x0d, 0x32,
|
||||
0x11, 0x1e, 0x4c, 0xaf, 0x23, 0x7e, 0xe5, 0x3c,
|
||||
0xa8, 0xad, 0x64, 0x26, 0x19, 0x4a, 0x88, 0x54,
|
||||
0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d,
|
||||
0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b,
|
||||
};
|
||||
const input = []u8{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
var result: [64]u8 = undefined;
|
||||
const key = []u8{
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
const nonce = []u8{ 1, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
|
||||
assert(mem.eql(u8, expected_result, result));
|
||||
}
|
||||
|
||||
test "crypto.chacha20 test vector 5" {
|
||||
const expected_result = []u8{
|
||||
0xf7, 0x98, 0xa1, 0x89, 0xf1, 0x95, 0xe6, 0x69,
|
||||
0x82, 0x10, 0x5f, 0xfb, 0x64, 0x0b, 0xb7, 0x75,
|
||||
0x7f, 0x57, 0x9d, 0xa3, 0x16, 0x02, 0xfc, 0x93,
|
||||
0xec, 0x01, 0xac, 0x56, 0xf8, 0x5a, 0xc3, 0xc1,
|
||||
0x34, 0xa4, 0x54, 0x7b, 0x73, 0x3b, 0x46, 0x41,
|
||||
0x30, 0x42, 0xc9, 0x44, 0x00, 0x49, 0x17, 0x69,
|
||||
0x05, 0xd3, 0xbe, 0x59, 0xea, 0x1c, 0x53, 0xf1,
|
||||
0x59, 0x16, 0x15, 0x5c, 0x2b, 0xe8, 0x24, 0x1a,
|
||||
|
||||
0x38, 0x00, 0x8b, 0x9a, 0x26, 0xbc, 0x35, 0x94,
|
||||
0x1e, 0x24, 0x44, 0x17, 0x7c, 0x8a, 0xde, 0x66,
|
||||
0x89, 0xde, 0x95, 0x26, 0x49, 0x86, 0xd9, 0x58,
|
||||
0x89, 0xfb, 0x60, 0xe8, 0x46, 0x29, 0xc9, 0xbd,
|
||||
0x9a, 0x5a, 0xcb, 0x1c, 0xc1, 0x18, 0xbe, 0x56,
|
||||
0x3e, 0xb9, 0xb3, 0xa4, 0xa4, 0x72, 0xf8, 0x2e,
|
||||
0x09, 0xa7, 0xe7, 0x78, 0x49, 0x2b, 0x56, 0x2e,
|
||||
0xf7, 0x13, 0x0e, 0x88, 0xdf, 0xe0, 0x31, 0xc7,
|
||||
|
||||
0x9d, 0xb9, 0xd4, 0xf7, 0xc7, 0xa8, 0x99, 0x15,
|
||||
0x1b, 0x9a, 0x47, 0x50, 0x32, 0xb6, 0x3f, 0xc3,
|
||||
0x85, 0x24, 0x5f, 0xe0, 0x54, 0xe3, 0xdd, 0x5a,
|
||||
0x97, 0xa5, 0xf5, 0x76, 0xfe, 0x06, 0x40, 0x25,
|
||||
0xd3, 0xce, 0x04, 0x2c, 0x56, 0x6a, 0xb2, 0xc5,
|
||||
0x07, 0xb1, 0x38, 0xdb, 0x85, 0x3e, 0x3d, 0x69,
|
||||
0x59, 0x66, 0x09, 0x96, 0x54, 0x6c, 0xc9, 0xc4,
|
||||
0xa6, 0xea, 0xfd, 0xc7, 0x77, 0xc0, 0x40, 0xd7,
|
||||
|
||||
0x0e, 0xaf, 0x46, 0xf7, 0x6d, 0xad, 0x39, 0x79,
|
||||
0xe5, 0xc5, 0x36, 0x0c, 0x33, 0x17, 0x16, 0x6a,
|
||||
0x1c, 0x89, 0x4c, 0x94, 0xa3, 0x71, 0x87, 0x6a,
|
||||
0x94, 0xdf, 0x76, 0x28, 0xfe, 0x4e, 0xaa, 0xf2,
|
||||
0xcc, 0xb2, 0x7d, 0x5a, 0xaa, 0xe0, 0xad, 0x7a,
|
||||
0xd0, 0xf9, 0xd4, 0xb6, 0xad, 0x3b, 0x54, 0x09,
|
||||
0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a,
|
||||
0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9,
|
||||
};
|
||||
const input = []u8{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
var result: [256]u8 = undefined;
|
||||
const key = []u8{
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
};
|
||||
const nonce = []u8{
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
};
|
||||
|
||||
chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
|
||||
assert(mem.eql(u8, expected_result, result));
|
||||
}
|
||||
@ -7,46 +7,63 @@ pub const HmacMd5 = Hmac(crypto.Md5);
|
||||
pub const HmacSha1 = Hmac(crypto.Sha1);
|
||||
pub const HmacSha256 = Hmac(crypto.Sha256);
|
||||
|
||||
pub fn Hmac(comptime H: type) type {
|
||||
pub fn Hmac(comptime Hash: type) type {
|
||||
return struct {
|
||||
const digest_size = H.digest_size;
|
||||
const Self = this;
|
||||
pub const mac_length = Hash.digest_length;
|
||||
pub const minimum_key_length = 0;
|
||||
|
||||
pub fn hash(output: []u8, key: []const u8, message: []const u8) void {
|
||||
debug.assert(output.len >= H.digest_size);
|
||||
debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
|
||||
var scratch: [H.block_size]u8 = undefined;
|
||||
o_key_pad: [Hash.block_length]u8,
|
||||
i_key_pad: [Hash.block_length]u8,
|
||||
scratch: [Hash.block_length]u8,
|
||||
hash: Hash,
|
||||
|
||||
// HMAC(k, m) = H(o_key_pad | H(i_key_pad | msg)) where | is concatenation
|
||||
pub fn create(out: []u8, msg: []const u8, key: []const u8) void {
|
||||
var ctx = Self.init(key);
|
||||
ctx.update(msg);
|
||||
ctx.final(out[0..]);
|
||||
}
|
||||
|
||||
pub fn init(key: []const u8) Self {
|
||||
var ctx: Self = undefined;
|
||||
|
||||
// Normalize key length to block size of hash
|
||||
if (key.len > H.block_size) {
|
||||
H.hash(key, scratch[0..H.digest_size]);
|
||||
mem.set(u8, scratch[H.digest_size..H.block_size], 0);
|
||||
} else if (key.len < H.block_size) {
|
||||
mem.copy(u8, scratch[0..key.len], key);
|
||||
mem.set(u8, scratch[key.len..H.block_size], 0);
|
||||
if (key.len > Hash.block_length) {
|
||||
Hash.hash(key, ctx.scratch[0..mac_length]);
|
||||
mem.set(u8, ctx.scratch[mac_length..Hash.block_length], 0);
|
||||
} else if (key.len < Hash.block_length) {
|
||||
mem.copy(u8, ctx.scratch[0..key.len], key);
|
||||
mem.set(u8, ctx.scratch[key.len..Hash.block_length], 0);
|
||||
} else {
|
||||
mem.copy(u8, scratch[0..], key);
|
||||
mem.copy(u8, ctx.scratch[0..], key);
|
||||
}
|
||||
|
||||
var o_key_pad: [H.block_size]u8 = undefined;
|
||||
for (o_key_pad) |*b, i| {
|
||||
b.* = scratch[i] ^ 0x5c;
|
||||
for (ctx.o_key_pad) |*b, i| {
|
||||
b.* = ctx.scratch[i] ^ 0x5c;
|
||||
}
|
||||
|
||||
var i_key_pad: [H.block_size]u8 = undefined;
|
||||
for (i_key_pad) |*b, i| {
|
||||
b.* = scratch[i] ^ 0x36;
|
||||
for (ctx.i_key_pad) |*b, i| {
|
||||
b.* = ctx.scratch[i] ^ 0x36;
|
||||
}
|
||||
|
||||
// HMAC(k, m) = H(o_key_pad | H(i_key_pad | message)) where | is concatenation
|
||||
var hmac = H.init();
|
||||
hmac.update(i_key_pad[0..]);
|
||||
hmac.update(message);
|
||||
hmac.final(scratch[0..H.digest_size]);
|
||||
ctx.hash = Hash.init();
|
||||
ctx.hash.update(ctx.i_key_pad[0..]);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
hmac.reset();
|
||||
hmac.update(o_key_pad[0..]);
|
||||
hmac.update(scratch[0..H.digest_size]);
|
||||
hmac.final(output[0..H.digest_size]);
|
||||
pub fn update(ctx: *Self, msg: []const u8) void {
|
||||
ctx.hash.update(msg);
|
||||
}
|
||||
|
||||
pub fn final(ctx: *Self, out: []u8) void {
|
||||
debug.assert(Hash.block_length >= out.len and out.len >= mac_length);
|
||||
|
||||
ctx.hash.final(ctx.scratch[0..mac_length]);
|
||||
ctx.hash.reset();
|
||||
ctx.hash.update(ctx.o_key_pad[0..]);
|
||||
ctx.hash.update(ctx.scratch[0..mac_length]);
|
||||
ctx.hash.final(out[0..mac_length]);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -54,28 +71,28 @@ pub fn Hmac(comptime H: type) type {
|
||||
const htest = @import("test.zig");
|
||||
|
||||
test "hmac md5" {
|
||||
var out: [crypto.Md5.digest_size]u8 = undefined;
|
||||
HmacMd5.hash(out[0..], "", "");
|
||||
var out: [HmacMd5.mac_length]u8 = undefined;
|
||||
HmacMd5.create(out[0..], "", "");
|
||||
htest.assertEqual("74e6f7298a9c2d168935f58c001bad88", out[0..]);
|
||||
|
||||
HmacMd5.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");
|
||||
HmacMd5.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
|
||||
htest.assertEqual("80070713463e7749b90c2dc24911e275", out[0..]);
|
||||
}
|
||||
|
||||
test "hmac sha1" {
|
||||
var out: [crypto.Sha1.digest_size]u8 = undefined;
|
||||
HmacSha1.hash(out[0..], "", "");
|
||||
var out: [HmacSha1.mac_length]u8 = undefined;
|
||||
HmacSha1.create(out[0..], "", "");
|
||||
htest.assertEqual("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d", out[0..]);
|
||||
|
||||
HmacSha1.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");
|
||||
HmacSha1.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
|
||||
htest.assertEqual("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", out[0..]);
|
||||
}
|
||||
|
||||
test "hmac sha256" {
|
||||
var out: [crypto.Sha256.digest_size]u8 = undefined;
|
||||
HmacSha256.hash(out[0..], "", "");
|
||||
var out: [HmacSha256.mac_length]u8 = undefined;
|
||||
HmacSha256.create(out[0..], "", "");
|
||||
htest.assertEqual("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", out[0..]);
|
||||
|
||||
HmacSha256.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");
|
||||
HmacSha256.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
|
||||
htest.assertEqual("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", out[0..]);
|
||||
}
|
||||
|
||||
@ -21,14 +21,24 @@ pub const Blake2b512 = blake2.Blake2b512;
|
||||
|
||||
const hmac = @import("hmac.zig");
|
||||
pub const HmacMd5 = hmac.HmacMd5;
|
||||
pub const HmacSha1 = hmac.Sha1;
|
||||
pub const HmacSha256 = hmac.Sha256;
|
||||
pub const HmacSha1 = hmac.HmacSha1;
|
||||
pub const HmacSha256 = hmac.HmacSha256;
|
||||
|
||||
const import_chaCha20 = @import("chacha20.zig");
|
||||
pub const chaCha20IETF = import_chaCha20.chaCha20IETF;
|
||||
pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce;
|
||||
|
||||
pub const Poly1305 = @import("poly1305.zig").Poly1305;
|
||||
pub const X25519 = @import("x25519.zig").X25519;
|
||||
|
||||
test "crypto" {
|
||||
_ = @import("blake2.zig");
|
||||
_ = @import("chacha20.zig");
|
||||
_ = @import("hmac.zig");
|
||||
_ = @import("md5.zig");
|
||||
_ = @import("poly1305.zig");
|
||||
_ = @import("sha1.zig");
|
||||
_ = @import("sha2.zig");
|
||||
_ = @import("sha3.zig");
|
||||
_ = @import("blake2.zig");
|
||||
_ = @import("hmac.zig");
|
||||
_ = @import("x25519.zig");
|
||||
}
|
||||
|
||||
@ -29,8 +29,8 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, k: usize, s: u32, t: u32) RoundPar
|
||||
|
||||
pub const Md5 = struct {
|
||||
const Self = this;
|
||||
const block_size = 64;
|
||||
const digest_size = 16;
|
||||
const block_length = 64;
|
||||
const digest_length = 16;
|
||||
|
||||
s: [4]u32,
|
||||
// Streaming Cache
|
||||
@ -271,8 +271,8 @@ test "md5 streaming" {
|
||||
}
|
||||
|
||||
test "md5 aligned final" {
|
||||
var block = []u8{0} ** Md5.block_size;
|
||||
var out: [Md5.digest_size]u8 = undefined;
|
||||
var block = []u8{0} ** Md5.block_length;
|
||||
var out: [Md5.digest_length]u8 = undefined;
|
||||
|
||||
var h = Md5.init();
|
||||
h.update(block);
|
||||
|
||||
233
std/crypto/poly1305.zig
Normal file
233
std/crypto/poly1305.zig
Normal file
@ -0,0 +1,233 @@
|
||||
// Translated from monocypher which is licensed under CC-0/BSD-3.
|
||||
//
|
||||
// https://monocypher.org/
|
||||
|
||||
const std = @import("../index.zig");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const Endian = builtin.Endian;
|
||||
const readInt = std.mem.readInt;
|
||||
const writeInt = std.mem.writeInt;
|
||||
|
||||
pub const Poly1305 = struct {
|
||||
const Self = this;
|
||||
|
||||
pub const mac_length = 16;
|
||||
pub const minimum_key_length = 32;
|
||||
|
||||
// constant multiplier (from the secret key)
|
||||
r: [4]u32,
|
||||
// accumulated hash
|
||||
h: [5]u32,
|
||||
// chunk of the message
|
||||
c: [5]u32,
|
||||
// random number added at the end (from the secret key)
|
||||
pad: [4]u32,
|
||||
// How many bytes are there in the chunk.
|
||||
c_idx: usize,
|
||||
|
||||
fn secureZero(self: *Self) void {
|
||||
std.mem.secureZero(u8, @ptrCast([*]u8, self)[0..@sizeOf(Poly1305)]);
|
||||
}
|
||||
|
||||
pub fn create(out: []u8, msg: []const u8, key: []const u8) void {
|
||||
std.debug.assert(out.len >= mac_length);
|
||||
std.debug.assert(key.len >= minimum_key_length);
|
||||
|
||||
var ctx = Poly1305.init(key);
|
||||
ctx.update(msg);
|
||||
ctx.final(out);
|
||||
}
|
||||
|
||||
// Initialize the MAC context.
|
||||
// - key.len is sufficient size.
|
||||
pub fn init(key: []const u8) Self {
|
||||
var ctx: Poly1305 = undefined;
|
||||
|
||||
// Initial hash is zero
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < 5) : (i += 1) {
|
||||
ctx.h[i] = 0;
|
||||
}
|
||||
}
|
||||
// add 2^130 to every input block
|
||||
ctx.c[4] = 1;
|
||||
polyClearC(&ctx);
|
||||
|
||||
// load r and pad (r has some of its bits cleared)
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < 1) : (i += 1) {
|
||||
ctx.r[0] = readInt(key[0..4], u32, Endian.Little) & 0x0fffffff;
|
||||
}
|
||||
}
|
||||
{
|
||||
var i: usize = 1;
|
||||
while (i < 4) : (i += 1) {
|
||||
ctx.r[i] = readInt(key[i * 4 .. i * 4 + 4], u32, Endian.Little) & 0x0ffffffc;
|
||||
}
|
||||
}
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < 4) : (i += 1) {
|
||||
ctx.pad[i] = readInt(key[i * 4 + 16 .. i * 4 + 16 + 4], u32, Endian.Little);
|
||||
}
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// h = (h + c) * r
|
||||
// preconditions:
|
||||
// ctx->h <= 4_ffffffff_ffffffff_ffffffff_ffffffff
|
||||
// ctx->c <= 1_ffffffff_ffffffff_ffffffff_ffffffff
|
||||
// ctx->r <= 0ffffffc_0ffffffc_0ffffffc_0fffffff
|
||||
// Postcondition:
|
||||
// ctx->h <= 4_ffffffff_ffffffff_ffffffff_ffffffff
|
||||
fn polyBlock(ctx: *Self) void {
|
||||
// s = h + c, without carry propagation
|
||||
const s0 = u64(ctx.h[0]) + ctx.c[0]; // s0 <= 1_fffffffe
|
||||
const s1 = u64(ctx.h[1]) + ctx.c[1]; // s1 <= 1_fffffffe
|
||||
const s2 = u64(ctx.h[2]) + ctx.c[2]; // s2 <= 1_fffffffe
|
||||
const s3 = u64(ctx.h[3]) + ctx.c[3]; // s3 <= 1_fffffffe
|
||||
const s4 = u64(ctx.h[4]) + ctx.c[4]; // s4 <= 5
|
||||
|
||||
// Local all the things!
|
||||
const r0 = ctx.r[0]; // r0 <= 0fffffff
|
||||
const r1 = ctx.r[1]; // r1 <= 0ffffffc
|
||||
const r2 = ctx.r[2]; // r2 <= 0ffffffc
|
||||
const r3 = ctx.r[3]; // r3 <= 0ffffffc
|
||||
const rr0 = (r0 >> 2) * 5; // rr0 <= 13fffffb // lose 2 bits...
|
||||
const rr1 = (r1 >> 2) + r1; // rr1 <= 13fffffb // rr1 == (r1 >> 2) * 5
|
||||
const rr2 = (r2 >> 2) + r2; // rr2 <= 13fffffb // rr1 == (r2 >> 2) * 5
|
||||
const rr3 = (r3 >> 2) + r3; // rr3 <= 13fffffb // rr1 == (r3 >> 2) * 5
|
||||
|
||||
// (h + c) * r, without carry propagation
|
||||
const x0 = s0 * r0 + s1 * rr3 + s2 * rr2 + s3 * rr1 + s4 * rr0; //<=97ffffe007fffff8
|
||||
const x1 = s0 * r1 + s1 * r0 + s2 * rr3 + s3 * rr2 + s4 * rr1; //<=8fffffe20ffffff6
|
||||
const x2 = s0 * r2 + s1 * r1 + s2 * r0 + s3 * rr3 + s4 * rr2; //<=87ffffe417fffff4
|
||||
const x3 = s0 * r3 + s1 * r2 + s2 * r1 + s3 * r0 + s4 * rr3; //<=7fffffe61ffffff2
|
||||
const x4 = s4 * (r0 & 3); // ...recover 2 bits //<= f
|
||||
|
||||
// partial reduction modulo 2^130 - 5
|
||||
const _u5 = @truncate(u32, x4 + (x3 >> 32)); // u5 <= 7ffffff5
|
||||
const _u0 = (_u5 >> 2) * 5 + (x0 & 0xffffffff);
|
||||
const _u1 = (_u0 >> 32) + (x1 & 0xffffffff) + (x0 >> 32);
|
||||
const _u2 = (_u1 >> 32) + (x2 & 0xffffffff) + (x1 >> 32);
|
||||
const _u3 = (_u2 >> 32) + (x3 & 0xffffffff) + (x2 >> 32);
|
||||
const _u4 = (_u3 >> 32) + (_u5 & 3);
|
||||
|
||||
// Update the hash
|
||||
ctx.h[0] = @truncate(u32, _u0); // u0 <= 1_9ffffff0
|
||||
ctx.h[1] = @truncate(u32, _u1); // u1 <= 1_97ffffe0
|
||||
ctx.h[2] = @truncate(u32, _u2); // u2 <= 1_8fffffe2
|
||||
ctx.h[3] = @truncate(u32, _u3); // u3 <= 1_87ffffe4
|
||||
ctx.h[4] = @truncate(u32, _u4); // u4 <= 4
|
||||
}
|
||||
|
||||
// (re-)initializes the input counter and input buffer
|
||||
fn polyClearC(ctx: *Self) void {
|
||||
ctx.c[0] = 0;
|
||||
ctx.c[1] = 0;
|
||||
ctx.c[2] = 0;
|
||||
ctx.c[3] = 0;
|
||||
ctx.c_idx = 0;
|
||||
}
|
||||
|
||||
fn polyTakeInput(ctx: *Self, input: u8) void {
|
||||
const word = ctx.c_idx >> 2;
|
||||
const byte = ctx.c_idx & 3;
|
||||
ctx.c[word] |= std.math.shl(u32, input, byte * 8);
|
||||
ctx.c_idx += 1;
|
||||
}
|
||||
|
||||
fn polyUpdate(ctx: *Self, msg: []const u8) void {
|
||||
for (msg) |b| {
|
||||
polyTakeInput(ctx, b);
|
||||
if (ctx.c_idx == 16) {
|
||||
polyBlock(ctx);
|
||||
polyClearC(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn alignTo(x: usize, block_size: usize) usize {
|
||||
return ((~x) +% 1) & (block_size - 1);
|
||||
}
|
||||
|
||||
// Feed data into the MAC context.
|
||||
pub fn update(ctx: *Self, msg: []const u8) void {
|
||||
// Align ourselves with block boundaries
|
||||
const alignm = std.math.min(alignTo(ctx.c_idx, 16), msg.len);
|
||||
polyUpdate(ctx, msg[0..alignm]);
|
||||
|
||||
var nmsg = msg[alignm..];
|
||||
|
||||
// Process the msg block by block
|
||||
const nb_blocks = nmsg.len >> 4;
|
||||
var i: usize = 0;
|
||||
while (i < nb_blocks) : (i += 1) {
|
||||
ctx.c[0] = readInt(nmsg[0..4], u32, Endian.Little);
|
||||
ctx.c[1] = readInt(nmsg[4..8], u32, Endian.Little);
|
||||
ctx.c[2] = readInt(nmsg[8..12], u32, Endian.Little);
|
||||
ctx.c[3] = readInt(nmsg[12..16], u32, Endian.Little);
|
||||
polyBlock(ctx);
|
||||
nmsg = nmsg[16..];
|
||||
}
|
||||
if (nb_blocks > 0) {
|
||||
polyClearC(ctx);
|
||||
}
|
||||
|
||||
// remaining bytes
|
||||
polyUpdate(ctx, nmsg[0..]);
|
||||
}
|
||||
|
||||
// Finalize the MAC and output into buffer provided by caller.
|
||||
pub fn final(ctx: *Self, out: []u8) void {
|
||||
// Process the last block (if any)
|
||||
if (ctx.c_idx != 0) {
|
||||
// move the final 1 according to remaining input length
|
||||
// (We may add less than 2^130 to the last input block)
|
||||
ctx.c[4] = 0;
|
||||
polyTakeInput(ctx, 1);
|
||||
// one last hash update
|
||||
polyBlock(ctx);
|
||||
}
|
||||
|
||||
// check if we should subtract 2^130-5 by performing the
|
||||
// corresponding carry propagation.
|
||||
const _u0 = u64(5) + ctx.h[0]; // <= 1_00000004
|
||||
const _u1 = (_u0 >> 32) + ctx.h[1]; // <= 1_00000000
|
||||
const _u2 = (_u1 >> 32) + ctx.h[2]; // <= 1_00000000
|
||||
const _u3 = (_u2 >> 32) + ctx.h[3]; // <= 1_00000000
|
||||
const _u4 = (_u3 >> 32) + ctx.h[4]; // <= 5
|
||||
// u4 indicates how many times we should subtract 2^130-5 (0 or 1)
|
||||
|
||||
// h + pad, minus 2^130-5 if u4 exceeds 3
|
||||
const uu0 = (_u4 >> 2) * 5 + ctx.h[0] + ctx.pad[0]; // <= 2_00000003
|
||||
const uu1 = (uu0 >> 32) + ctx.h[1] + ctx.pad[1]; // <= 2_00000000
|
||||
const uu2 = (uu1 >> 32) + ctx.h[2] + ctx.pad[2]; // <= 2_00000000
|
||||
const uu3 = (uu2 >> 32) + ctx.h[3] + ctx.pad[3]; // <= 2_00000000
|
||||
|
||||
writeInt(out[0..], @truncate(u32, uu0), Endian.Little);
|
||||
writeInt(out[4..], @truncate(u32, uu1), Endian.Little);
|
||||
writeInt(out[8..], @truncate(u32, uu2), Endian.Little);
|
||||
writeInt(out[12..], @truncate(u32, uu3), Endian.Little);
|
||||
|
||||
ctx.secureZero();
|
||||
}
|
||||
};
|
||||
|
||||
test "poly1305 rfc7439 vector1" {
|
||||
const expected_mac = "\xa8\x06\x1d\xc1\x30\x51\x36\xc6\xc2\x2b\x8b\xaf\x0c\x01\x27\xa9";
|
||||
|
||||
const msg = "Cryptographic Forum Research Group";
|
||||
const key = "\x85\xd6\xbe\x78\x57\x55\x6d\x33\x7f\x44\x52\xfe\x42\xd5\x06\xa8" ++
|
||||
"\x01\x03\x80\x8a\xfb\x0d\xb2\xfd\x4a\xbf\xf6\xaf\x41\x49\xf5\x1b";
|
||||
|
||||
var mac: [16]u8 = undefined;
|
||||
Poly1305.create(mac[0..], msg, key);
|
||||
|
||||
std.debug.assert(std.mem.eql(u8, mac, expected_mac));
|
||||
}
|
||||
@ -26,8 +26,8 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam {
|
||||
|
||||
pub const Sha1 = struct {
|
||||
const Self = this;
|
||||
const block_size = 64;
|
||||
const digest_size = 20;
|
||||
const block_length = 64;
|
||||
const digest_length = 20;
|
||||
|
||||
s: [5]u32,
|
||||
// Streaming Cache
|
||||
@ -292,8 +292,8 @@ test "sha1 streaming" {
|
||||
}
|
||||
|
||||
test "sha1 aligned final" {
|
||||
var block = []u8{0} ** Sha1.block_size;
|
||||
var out: [Sha1.digest_size]u8 = undefined;
|
||||
var block = []u8{0} ** Sha1.block_length;
|
||||
var out: [Sha1.digest_length]u8 = undefined;
|
||||
|
||||
var h = Sha1.init();
|
||||
h.update(block);
|
||||
|
||||
@ -78,8 +78,8 @@ pub const Sha256 = Sha2_32(Sha256Params);
|
||||
fn Sha2_32(comptime params: Sha2Params32) type {
|
||||
return struct {
|
||||
const Self = this;
|
||||
const block_size = 64;
|
||||
const digest_size = params.out_len / 8;
|
||||
const block_length = 64;
|
||||
const digest_length = params.out_len / 8;
|
||||
|
||||
s: [8]u32,
|
||||
// Streaming Cache
|
||||
@ -338,8 +338,8 @@ test "sha256 streaming" {
|
||||
}
|
||||
|
||||
test "sha256 aligned final" {
|
||||
var block = []u8{0} ** Sha256.block_size;
|
||||
var out: [Sha256.digest_size]u8 = undefined;
|
||||
var block = []u8{0} ** Sha256.block_length;
|
||||
var out: [Sha256.digest_length]u8 = undefined;
|
||||
|
||||
var h = Sha256.init();
|
||||
h.update(block);
|
||||
@ -419,8 +419,8 @@ pub const Sha512 = Sha2_64(Sha512Params);
|
||||
fn Sha2_64(comptime params: Sha2Params64) type {
|
||||
return struct {
|
||||
const Self = this;
|
||||
const block_size = 128;
|
||||
const digest_size = params.out_len / 8;
|
||||
const block_length = 128;
|
||||
const digest_length = params.out_len / 8;
|
||||
|
||||
s: [8]u64,
|
||||
// Streaming Cache
|
||||
@ -715,8 +715,8 @@ test "sha512 streaming" {
|
||||
}
|
||||
|
||||
test "sha512 aligned final" {
|
||||
var block = []u8{0} ** Sha512.block_size;
|
||||
var out: [Sha512.digest_size]u8 = undefined;
|
||||
var block = []u8{0} ** Sha512.block_length;
|
||||
var out: [Sha512.digest_length]u8 = undefined;
|
||||
|
||||
var h = Sha512.init();
|
||||
h.update(block);
|
||||
|
||||
@ -13,8 +13,8 @@ pub const Sha3_512 = Keccak(512, 0x06);
|
||||
fn Keccak(comptime bits: usize, comptime delim: u8) type {
|
||||
return struct {
|
||||
const Self = this;
|
||||
const block_size = 200;
|
||||
const digest_size = bits / 8;
|
||||
const block_length = 200;
|
||||
const digest_length = bits / 8;
|
||||
|
||||
s: [200]u8,
|
||||
offset: usize,
|
||||
@ -87,97 +87,24 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type {
|
||||
}
|
||||
|
||||
const RC = []const u64{
|
||||
0x0000000000000001,
|
||||
0x0000000000008082,
|
||||
0x800000000000808a,
|
||||
0x8000000080008000,
|
||||
0x000000000000808b,
|
||||
0x0000000080000001,
|
||||
0x8000000080008081,
|
||||
0x8000000000008009,
|
||||
0x000000000000008a,
|
||||
0x0000000000000088,
|
||||
0x0000000080008009,
|
||||
0x000000008000000a,
|
||||
0x000000008000808b,
|
||||
0x800000000000008b,
|
||||
0x8000000000008089,
|
||||
0x8000000000008003,
|
||||
0x8000000000008002,
|
||||
0x8000000000000080,
|
||||
0x000000000000800a,
|
||||
0x800000008000000a,
|
||||
0x8000000080008081,
|
||||
0x8000000000008080,
|
||||
0x0000000080000001,
|
||||
0x8000000080008008,
|
||||
0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
|
||||
0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
|
||||
0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
|
||||
0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
|
||||
0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
|
||||
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
|
||||
};
|
||||
|
||||
const ROTC = []const usize{
|
||||
1,
|
||||
3,
|
||||
6,
|
||||
10,
|
||||
15,
|
||||
21,
|
||||
28,
|
||||
36,
|
||||
45,
|
||||
55,
|
||||
2,
|
||||
14,
|
||||
27,
|
||||
41,
|
||||
56,
|
||||
8,
|
||||
25,
|
||||
43,
|
||||
62,
|
||||
18,
|
||||
39,
|
||||
61,
|
||||
20,
|
||||
44,
|
||||
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
|
||||
};
|
||||
|
||||
const PIL = []const usize{
|
||||
10,
|
||||
7,
|
||||
11,
|
||||
17,
|
||||
18,
|
||||
3,
|
||||
5,
|
||||
16,
|
||||
8,
|
||||
21,
|
||||
24,
|
||||
4,
|
||||
15,
|
||||
23,
|
||||
19,
|
||||
13,
|
||||
12,
|
||||
2,
|
||||
20,
|
||||
14,
|
||||
22,
|
||||
9,
|
||||
6,
|
||||
1,
|
||||
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
|
||||
};
|
||||
|
||||
const M5 = []const usize{
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
0, 1, 2, 3, 4, 0, 1, 2, 3, 4,
|
||||
};
|
||||
|
||||
fn keccak_f(comptime F: usize, d: []u8) void {
|
||||
@ -297,8 +224,8 @@ test "sha3-256 streaming" {
|
||||
}
|
||||
|
||||
test "sha3-256 aligned final" {
|
||||
var block = []u8{0} ** Sha3_256.block_size;
|
||||
var out: [Sha3_256.digest_size]u8 = undefined;
|
||||
var block = []u8{0} ** Sha3_256.block_length;
|
||||
var out: [Sha3_256.digest_length]u8 = undefined;
|
||||
|
||||
var h = Sha3_256.init();
|
||||
h.update(block);
|
||||
@ -368,8 +295,8 @@ test "sha3-512 streaming" {
|
||||
}
|
||||
|
||||
test "sha3-512 aligned final" {
|
||||
var block = []u8{0} ** Sha3_512.block_size;
|
||||
var out: [Sha3_512.digest_size]u8 = undefined;
|
||||
var block = []u8{0} ** Sha3_512.block_length;
|
||||
var out: [Sha3_512.digest_length]u8 = undefined;
|
||||
|
||||
var h = Sha3_512.init();
|
||||
h.update(block);
|
||||
|
||||
@ -1,38 +1,193 @@
|
||||
// Modify the HashFunction variable to the one wanted to test.
|
||||
//
|
||||
// ```
|
||||
// zig build-exe --release-fast throughput_test.zig
|
||||
// ./throughput_test
|
||||
// ```
|
||||
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const time = std.os.time;
|
||||
const Timer = time.Timer;
|
||||
const HashFunction = @import("md5.zig").Md5;
|
||||
const crypto = @import("index.zig");
|
||||
|
||||
const MiB = 1024 * 1024;
|
||||
const BytesToHash = 1024 * MiB;
|
||||
const KiB = 1024;
|
||||
const MiB = 1024 * KiB;
|
||||
|
||||
var prng = std.rand.DefaultPrng.init(0);
|
||||
|
||||
const Crypto = struct {
|
||||
ty: type,
|
||||
name: []const u8,
|
||||
};
|
||||
|
||||
const hashes = []Crypto{
|
||||
Crypto{ .ty = crypto.Md5, .name = "md5" },
|
||||
Crypto{ .ty = crypto.Sha1, .name = "sha1" },
|
||||
Crypto{ .ty = crypto.Sha256, .name = "sha256" },
|
||||
Crypto{ .ty = crypto.Sha512, .name = "sha512" },
|
||||
Crypto{ .ty = crypto.Sha3_256, .name = "sha3-256" },
|
||||
Crypto{ .ty = crypto.Sha3_512, .name = "sha3-512" },
|
||||
Crypto{ .ty = crypto.Blake2s256, .name = "blake2s" },
|
||||
Crypto{ .ty = crypto.Blake2b512, .name = "blake2b" },
|
||||
};
|
||||
|
||||
pub fn benchmarkHash(comptime Hash: var, comptime bytes: comptime_int) !u64 {
|
||||
var h = Hash.init();
|
||||
|
||||
var block: [Hash.digest_length]u8 = undefined;
|
||||
prng.random.bytes(block[0..]);
|
||||
|
||||
var offset: usize = 0;
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (offset < bytes) : (offset += block.len) {
|
||||
h.update(block[0..]);
|
||||
}
|
||||
const end = timer.read();
|
||||
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
const throughput = @floatToInt(u64, bytes / elapsed_s);
|
||||
|
||||
return throughput;
|
||||
}
|
||||
|
||||
const macs = []Crypto{
|
||||
Crypto{ .ty = crypto.Poly1305, .name = "poly1305" },
|
||||
Crypto{ .ty = crypto.HmacMd5, .name = "hmac-md5" },
|
||||
Crypto{ .ty = crypto.HmacSha1, .name = "hmac-sha1" },
|
||||
Crypto{ .ty = crypto.HmacSha256, .name = "hmac-sha256" },
|
||||
};
|
||||
|
||||
pub fn benchmarkMac(comptime Mac: var, comptime bytes: comptime_int) !u64 {
|
||||
std.debug.assert(32 >= Mac.mac_length and 32 >= Mac.minimum_key_length);
|
||||
|
||||
var in: [1 * MiB]u8 = undefined;
|
||||
prng.random.bytes(in[0..]);
|
||||
|
||||
var key: [32]u8 = undefined;
|
||||
prng.random.bytes(key[0..]);
|
||||
|
||||
var offset: usize = 0;
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (offset < bytes) : (offset += in.len) {
|
||||
Mac.create(key[0..], in[0..], key);
|
||||
}
|
||||
const end = timer.read();
|
||||
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
const throughput = @floatToInt(u64, bytes / elapsed_s);
|
||||
|
||||
return throughput;
|
||||
}
|
||||
|
||||
const exchanges = []Crypto{Crypto{ .ty = crypto.X25519, .name = "x25519" }};
|
||||
|
||||
pub fn benchmarkKeyExchange(comptime DhKeyExchange: var, comptime exchange_count: comptime_int) !u64 {
|
||||
std.debug.assert(DhKeyExchange.minimum_key_length >= DhKeyExchange.secret_length);
|
||||
|
||||
var in: [DhKeyExchange.minimum_key_length]u8 = undefined;
|
||||
prng.random.bytes(in[0..]);
|
||||
|
||||
var out: [DhKeyExchange.minimum_key_length]u8 = undefined;
|
||||
prng.random.bytes(out[0..]);
|
||||
|
||||
var offset: usize = 0;
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < exchange_count) : (i += 1) {
|
||||
_ = DhKeyExchange.create(out[0..], out, in);
|
||||
}
|
||||
}
|
||||
const end = timer.read();
|
||||
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
const throughput = @floatToInt(u64, exchange_count / elapsed_s);
|
||||
|
||||
return throughput;
|
||||
}
|
||||
|
||||
fn usage() void {
|
||||
std.debug.warn(
|
||||
\\throughput_test [options]
|
||||
\\
|
||||
\\Options:
|
||||
\\ --filter [test-name]
|
||||
\\ --seed [int]
|
||||
\\ --help
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
fn mode(comptime x: comptime_int) comptime_int {
|
||||
return if (builtin.mode == builtin.Mode.Debug) x / 64 else x;
|
||||
}
|
||||
|
||||
// TODO(#1358): Replace with builtin formatted padding when available.
|
||||
fn printPad(stdout: var, s: []const u8) !void {
|
||||
var i: usize = 0;
|
||||
while (i < 12 - s.len) : (i += 1) {
|
||||
try stdout.print(" ");
|
||||
}
|
||||
try stdout.print("{}", s);
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var stdout_file = try std.io.getStdOut();
|
||||
var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
|
||||
const stdout = &stdout_out_stream.stream;
|
||||
|
||||
var block: [HashFunction.block_size]u8 = undefined;
|
||||
std.mem.set(u8, block[0..], 0);
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
|
||||
const args = try std.os.argsAlloc(&fixed.allocator);
|
||||
|
||||
var h = HashFunction.init();
|
||||
var offset: usize = 0;
|
||||
var filter: ?[]u8 = "";
|
||||
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (offset < BytesToHash) : (offset += block.len) {
|
||||
h.update(block[0..]);
|
||||
var i: usize = 1;
|
||||
while (i < args.len) : (i += 1) {
|
||||
if (std.mem.eql(u8, args[i], "--seed")) {
|
||||
i += 1;
|
||||
if (i == args.len) {
|
||||
usage();
|
||||
std.os.exit(1);
|
||||
}
|
||||
|
||||
const seed = try std.fmt.parseUnsigned(u32, args[i], 10);
|
||||
prng.seed(seed);
|
||||
} else if (std.mem.eql(u8, args[i], "--filter")) {
|
||||
i += 1;
|
||||
if (i == args.len) {
|
||||
usage();
|
||||
std.os.exit(1);
|
||||
}
|
||||
|
||||
filter = args[i];
|
||||
} else if (std.mem.eql(u8, args[i], "--help")) {
|
||||
usage();
|
||||
return;
|
||||
} else {
|
||||
usage();
|
||||
std.os.exit(1);
|
||||
}
|
||||
}
|
||||
const end = timer.read();
|
||||
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
const throughput = @floatToInt(u64, BytesToHash / elapsed_s);
|
||||
inline for (hashes) |H| {
|
||||
if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
|
||||
const throughput = try benchmarkHash(H.ty, mode(32 * MiB));
|
||||
try printPad(stdout, H.name);
|
||||
try stdout.print(": {} MiB/s\n", throughput / (1 * MiB));
|
||||
}
|
||||
}
|
||||
|
||||
try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB));
|
||||
inline for (macs) |M| {
|
||||
if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {
|
||||
const throughput = try benchmarkMac(M.ty, mode(128 * MiB));
|
||||
try printPad(stdout, M.name);
|
||||
try stdout.print(": {} MiB/s\n", throughput / (1 * MiB));
|
||||
}
|
||||
}
|
||||
|
||||
inline for (exchanges) |E| {
|
||||
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
|
||||
const throughput = try benchmarkKeyExchange(E.ty, mode(1000));
|
||||
try printPad(stdout, E.name);
|
||||
try stdout.print(": {} exchanges/s\n", throughput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
664
std/crypto/x25519.zig
Normal file
664
std/crypto/x25519.zig
Normal file
@ -0,0 +1,664 @@
|
||||
// Translated from monocypher which is licensed under CC-0/BSD-3.
|
||||
//
|
||||
// https://monocypher.org/
|
||||
|
||||
const std = @import("../index.zig");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const Endian = builtin.Endian;
|
||||
const readInt = std.mem.readInt;
|
||||
const writeInt = std.mem.writeInt;
|
||||
|
||||
// Based on Supercop's ref10 implementation.
|
||||
pub const X25519 = struct {
|
||||
pub const secret_length = 32;
|
||||
pub const minimum_key_length = 32;
|
||||
|
||||
fn trimScalar(s: []u8) void {
|
||||
s[0] &= 248;
|
||||
s[31] &= 127;
|
||||
s[31] |= 64;
|
||||
}
|
||||
|
||||
fn scalarBit(s: []const u8, i: usize) i32 {
|
||||
return (s[i >> 3] >> @intCast(u3, i & 7)) & 1;
|
||||
}
|
||||
|
||||
pub fn create(out: []u8, private_key: []const u8, public_key: []const u8) bool {
|
||||
std.debug.assert(out.len >= secret_length);
|
||||
std.debug.assert(private_key.len >= minimum_key_length);
|
||||
std.debug.assert(public_key.len >= minimum_key_length);
|
||||
|
||||
var storage: [7]Fe = undefined;
|
||||
var x1 = &storage[0];
|
||||
var x2 = &storage[1];
|
||||
var z2 = &storage[2];
|
||||
var x3 = &storage[3];
|
||||
var z3 = &storage[4];
|
||||
var t0 = &storage[5];
|
||||
var t1 = &storage[6];
|
||||
|
||||
// computes the scalar product
|
||||
Fe.fromBytes(x1, public_key);
|
||||
|
||||
// restrict the possible scalar values
|
||||
var e: [32]u8 = undefined;
|
||||
for (e[0..]) |_, i| {
|
||||
e[i] = private_key[i];
|
||||
}
|
||||
trimScalar(e[0..]);
|
||||
|
||||
// computes the actual scalar product (the result is in x2 and z2)
|
||||
|
||||
// Montgomery ladder
|
||||
// In projective coordinates, to avoid divisons: x = X / Z
|
||||
// We don't care about the y coordinate, it's only 1 bit of information
|
||||
Fe.init1(x2);
|
||||
Fe.init0(z2); // "zero" point
|
||||
Fe.copy(x3, x1);
|
||||
Fe.init1(z3);
|
||||
|
||||
var swap: i32 = 0;
|
||||
var pos: isize = 254;
|
||||
while (pos >= 0) : (pos -= 1) {
|
||||
// constant time conditional swap before ladder step
|
||||
const b = scalarBit(e, @intCast(usize, pos));
|
||||
swap ^= b; // xor trick avoids swapping at the end of the loop
|
||||
Fe.cswap(x2, x3, swap);
|
||||
Fe.cswap(z2, z3, swap);
|
||||
swap = b; // anticipates one last swap after the loop
|
||||
|
||||
// Montgomery ladder step: replaces (P2, P3) by (P2*2, P2+P3)
|
||||
// with differential addition
|
||||
Fe.sub(t0, x3, z3);
|
||||
Fe.sub(t1, x2, z2);
|
||||
Fe.add(x2, x2, z2);
|
||||
Fe.add(z2, x3, z3);
|
||||
Fe.mul(z3, t0, x2);
|
||||
Fe.mul(z2, z2, t1);
|
||||
Fe.sq(t0, t1);
|
||||
Fe.sq(t1, x2);
|
||||
Fe.add(x3, z3, z2);
|
||||
Fe.sub(z2, z3, z2);
|
||||
Fe.mul(x2, t1, t0);
|
||||
Fe.sub(t1, t1, t0);
|
||||
Fe.sq(z2, z2);
|
||||
Fe.mulSmall(z3, t1, 121666);
|
||||
Fe.sq(x3, x3);
|
||||
Fe.add(t0, t0, z3);
|
||||
Fe.mul(z3, x1, z2);
|
||||
Fe.mul(z2, t1, t0);
|
||||
}
|
||||
|
||||
// last swap is necessary to compensate for the xor trick
|
||||
// Note: after this swap, P3 == P2 + P1.
|
||||
Fe.cswap(x2, x3, swap);
|
||||
Fe.cswap(z2, z3, swap);
|
||||
|
||||
// normalises the coordinates: x == X / Z
|
||||
Fe.invert(z2, z2);
|
||||
Fe.mul(x2, x2, z2);
|
||||
Fe.toBytes(out, x2);
|
||||
|
||||
x1.secureZero();
|
||||
x2.secureZero();
|
||||
x3.secureZero();
|
||||
t0.secureZero();
|
||||
t1.secureZero();
|
||||
z2.secureZero();
|
||||
z3.secureZero();
|
||||
std.mem.secureZero(u8, e[0..]);
|
||||
|
||||
// Returns false if the output is all zero
|
||||
// (happens with some malicious public keys)
|
||||
return !zerocmp(u8, out);
|
||||
}
|
||||
|
||||
pub fn createPublicKey(public_key: []const u8, private_key: []const u8) bool {
|
||||
var base_point = []u8{9} ++ []u8{0} ** 31;
|
||||
return create(public_key, private_key, base_point);
|
||||
}
|
||||
};
|
||||
|
||||
// Constant time compare to zero.
|
||||
fn zerocmp(comptime T: type, a: []const T) bool {
|
||||
var s: T = 0;
|
||||
for (a) |b| {
|
||||
s |= b;
|
||||
}
|
||||
return s == 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////
|
||||
/// Arithmetic modulo 2^255 - 19 ///
|
||||
////////////////////////////////////
|
||||
// Taken from Supercop's ref10 implementation.
|
||||
// A bit bigger than TweetNaCl, over 4 times faster.
|
||||
|
||||
// field element
|
||||
const Fe = struct {
|
||||
b: [10]i32,
|
||||
|
||||
fn secureZero(self: *Fe) void {
|
||||
std.mem.secureZero(u8, @ptrCast([*]u8, self)[0..@sizeOf(Fe)]);
|
||||
}
|
||||
|
||||
fn init0(h: *Fe) void {
|
||||
for (h.b) |*e| {
|
||||
e.* = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn init1(h: *Fe) void {
|
||||
for (h.b[1..]) |*e| {
|
||||
e.* = 0;
|
||||
}
|
||||
h.b[0] = 1;
|
||||
}
|
||||
|
||||
fn copy(h: *Fe, f: *const Fe) void {
|
||||
for (h.b) |_, i| {
|
||||
h.b[i] = f.b[i];
|
||||
}
|
||||
}
|
||||
|
||||
fn neg(h: *Fe, f: *const Fe) void {
|
||||
for (h.b) |_, i| {
|
||||
h.b[i] = -f.b[i];
|
||||
}
|
||||
}
|
||||
|
||||
fn add(h: *Fe, f: *const Fe, g: *const Fe) void {
|
||||
for (h.b) |_, i| {
|
||||
h.b[i] = f.b[i] + g.b[i];
|
||||
}
|
||||
}
|
||||
|
||||
fn sub(h: *Fe, f: *const Fe, g: *const Fe) void {
|
||||
for (h.b) |_, i| {
|
||||
h.b[i] = f.b[i] - g.b[i];
|
||||
}
|
||||
}
|
||||
|
||||
fn cswap(f: *Fe, g: *Fe, b: i32) void {
|
||||
for (f.b) |_, i| {
|
||||
const x = (f.b[i] ^ g.b[i]) & -b;
|
||||
f.b[i] ^= x;
|
||||
g.b[i] ^= x;
|
||||
}
|
||||
}
|
||||
|
||||
fn ccopy(f: *Fe, g: *const Fe, b: i32) void {
|
||||
for (f.b) |_, i| {
|
||||
const x = (f.b[i] ^ g.b[i]) & -b;
|
||||
f.b[i] ^= x;
|
||||
}
|
||||
}
|
||||
|
||||
inline fn carryRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int, comptime mult: comptime_int) void {
|
||||
const j = (i + 1) % 10;
|
||||
|
||||
c[i] = (t[i] + (i64(1) << shift)) >> (shift + 1);
|
||||
t[j] += c[i] * mult;
|
||||
t[i] -= c[i] * (i64(1) << (shift + 1));
|
||||
}
|
||||
|
||||
fn carry1(h: *Fe, t: []i64) void {
|
||||
var c: [10]i64 = undefined;
|
||||
|
||||
var sc = c[0..];
|
||||
var st = t[0..];
|
||||
|
||||
carryRound(sc, st, 9, 24, 19);
|
||||
carryRound(sc, st, 1, 24, 1);
|
||||
carryRound(sc, st, 3, 24, 1);
|
||||
carryRound(sc, st, 5, 24, 1);
|
||||
carryRound(sc, st, 7, 24, 1);
|
||||
carryRound(sc, st, 0, 25, 1);
|
||||
carryRound(sc, st, 2, 25, 1);
|
||||
carryRound(sc, st, 4, 25, 1);
|
||||
carryRound(sc, st, 6, 25, 1);
|
||||
carryRound(sc, st, 8, 25, 1);
|
||||
|
||||
for (h.b) |_, i| {
|
||||
h.b[i] = @intCast(i32, t[i]);
|
||||
}
|
||||
}
|
||||
|
||||
fn carry2(h: *Fe, t: []i64) void {
|
||||
var c: [10]i64 = undefined;
|
||||
|
||||
var sc = c[0..];
|
||||
var st = t[0..];
|
||||
|
||||
carryRound(sc, st, 0, 25, 1);
|
||||
carryRound(sc, st, 4, 25, 1);
|
||||
carryRound(sc, st, 1, 24, 1);
|
||||
carryRound(sc, st, 5, 24, 1);
|
||||
carryRound(sc, st, 2, 25, 1);
|
||||
carryRound(sc, st, 6, 25, 1);
|
||||
carryRound(sc, st, 3, 24, 1);
|
||||
carryRound(sc, st, 7, 24, 1);
|
||||
carryRound(sc, st, 4, 25, 1);
|
||||
carryRound(sc, st, 8, 25, 1);
|
||||
carryRound(sc, st, 9, 24, 19);
|
||||
carryRound(sc, st, 0, 25, 1);
|
||||
|
||||
for (h.b) |_, i| {
|
||||
h.b[i] = @intCast(i32, t[i]);
|
||||
}
|
||||
}
|
||||
|
||||
fn fromBytes(h: *Fe, s: []const u8) void {
|
||||
std.debug.assert(s.len >= 32);
|
||||
|
||||
var t: [10]i64 = undefined;
|
||||
|
||||
t[0] = readInt(s[0..4], u32, Endian.Little);
|
||||
t[1] = readInt(s[4..7], u32, Endian.Little) << 6;
|
||||
t[2] = readInt(s[7..10], u32, Endian.Little) << 5;
|
||||
t[3] = readInt(s[10..13], u32, Endian.Little) << 3;
|
||||
t[4] = readInt(s[13..16], u32, Endian.Little) << 2;
|
||||
t[5] = readInt(s[16..20], u32, Endian.Little);
|
||||
t[6] = readInt(s[20..23], u32, Endian.Little) << 7;
|
||||
t[7] = readInt(s[23..26], u32, Endian.Little) << 5;
|
||||
t[8] = readInt(s[26..29], u32, Endian.Little) << 4;
|
||||
t[9] = (readInt(s[29..32], u32, Endian.Little) & 0x7fffff) << 2;
|
||||
|
||||
carry1(h, t[0..]);
|
||||
}
|
||||
|
||||
fn mulSmall(h: *Fe, f: *const Fe, comptime g: comptime_int) void {
|
||||
var t: [10]i64 = undefined;
|
||||
|
||||
for (t[0..]) |_, i| {
|
||||
t[i] = i64(f.b[i]) * g;
|
||||
}
|
||||
|
||||
carry1(h, t[0..]);
|
||||
}
|
||||
|
||||
fn mul(h: *Fe, f1: *const Fe, g1: *const Fe) void {
|
||||
const f = f1.b;
|
||||
const g = g1.b;
|
||||
|
||||
var F: [10]i32 = undefined;
|
||||
var G: [10]i32 = undefined;
|
||||
|
||||
F[1] = f[1] * 2;
|
||||
F[3] = f[3] * 2;
|
||||
F[5] = f[5] * 2;
|
||||
F[7] = f[7] * 2;
|
||||
F[9] = f[9] * 2;
|
||||
|
||||
G[1] = g[1] * 19;
|
||||
G[2] = g[2] * 19;
|
||||
G[3] = g[3] * 19;
|
||||
G[4] = g[4] * 19;
|
||||
G[5] = g[5] * 19;
|
||||
G[6] = g[6] * 19;
|
||||
G[7] = g[7] * 19;
|
||||
G[8] = g[8] * 19;
|
||||
G[9] = g[9] * 19;
|
||||
|
||||
// t's become h
|
||||
var t: [10]i64 = undefined;
|
||||
|
||||
t[0] = f[0] * i64(g[0]) + F[1] * i64(G[9]) + f[2] * i64(G[8]) + F[3] * i64(G[7]) + f[4] * i64(G[6]) + F[5] * i64(G[5]) + f[6] * i64(G[4]) + F[7] * i64(G[3]) + f[8] * i64(G[2]) + F[9] * i64(G[1]);
|
||||
t[1] = f[0] * i64(g[1]) + f[1] * i64(g[0]) + f[2] * i64(G[9]) + f[3] * i64(G[8]) + f[4] * i64(G[7]) + f[5] * i64(G[6]) + f[6] * i64(G[5]) + f[7] * i64(G[4]) + f[8] * i64(G[3]) + f[9] * i64(G[2]);
|
||||
t[2] = f[0] * i64(g[2]) + F[1] * i64(g[1]) + f[2] * i64(g[0]) + F[3] * i64(G[9]) + f[4] * i64(G[8]) + F[5] * i64(G[7]) + f[6] * i64(G[6]) + F[7] * i64(G[5]) + f[8] * i64(G[4]) + F[9] * i64(G[3]);
|
||||
t[3] = f[0] * i64(g[3]) + f[1] * i64(g[2]) + f[2] * i64(g[1]) + f[3] * i64(g[0]) + f[4] * i64(G[9]) + f[5] * i64(G[8]) + f[6] * i64(G[7]) + f[7] * i64(G[6]) + f[8] * i64(G[5]) + f[9] * i64(G[4]);
|
||||
t[4] = f[0] * i64(g[4]) + F[1] * i64(g[3]) + f[2] * i64(g[2]) + F[3] * i64(g[1]) + f[4] * i64(g[0]) + F[5] * i64(G[9]) + f[6] * i64(G[8]) + F[7] * i64(G[7]) + f[8] * i64(G[6]) + F[9] * i64(G[5]);
|
||||
t[5] = f[0] * i64(g[5]) + f[1] * i64(g[4]) + f[2] * i64(g[3]) + f[3] * i64(g[2]) + f[4] * i64(g[1]) + f[5] * i64(g[0]) + f[6] * i64(G[9]) + f[7] * i64(G[8]) + f[8] * i64(G[7]) + f[9] * i64(G[6]);
|
||||
t[6] = f[0] * i64(g[6]) + F[1] * i64(g[5]) + f[2] * i64(g[4]) + F[3] * i64(g[3]) + f[4] * i64(g[2]) + F[5] * i64(g[1]) + f[6] * i64(g[0]) + F[7] * i64(G[9]) + f[8] * i64(G[8]) + F[9] * i64(G[7]);
|
||||
t[7] = f[0] * i64(g[7]) + f[1] * i64(g[6]) + f[2] * i64(g[5]) + f[3] * i64(g[4]) + f[4] * i64(g[3]) + f[5] * i64(g[2]) + f[6] * i64(g[1]) + f[7] * i64(g[0]) + f[8] * i64(G[9]) + f[9] * i64(G[8]);
|
||||
t[8] = f[0] * i64(g[8]) + F[1] * i64(g[7]) + f[2] * i64(g[6]) + F[3] * i64(g[5]) + f[4] * i64(g[4]) + F[5] * i64(g[3]) + f[6] * i64(g[2]) + F[7] * i64(g[1]) + f[8] * i64(g[0]) + F[9] * i64(G[9]);
|
||||
t[9] = f[0] * i64(g[9]) + f[1] * i64(g[8]) + f[2] * i64(g[7]) + f[3] * i64(g[6]) + f[4] * i64(g[5]) + f[5] * i64(g[4]) + f[6] * i64(g[3]) + f[7] * i64(g[2]) + f[8] * i64(g[1]) + f[9] * i64(g[0]);
|
||||
|
||||
carry2(h, t[0..]);
|
||||
}
|
||||
|
||||
// we could use Fe.mul() for this, but this is significantly faster
|
||||
fn sq(h: *Fe, fz: *const Fe) void {
|
||||
const f0 = fz.b[0];
|
||||
const f1 = fz.b[1];
|
||||
const f2 = fz.b[2];
|
||||
const f3 = fz.b[3];
|
||||
const f4 = fz.b[4];
|
||||
const f5 = fz.b[5];
|
||||
const f6 = fz.b[6];
|
||||
const f7 = fz.b[7];
|
||||
const f8 = fz.b[8];
|
||||
const f9 = fz.b[9];
|
||||
|
||||
const f0_2 = f0 * 2;
|
||||
const f1_2 = f1 * 2;
|
||||
const f2_2 = f2 * 2;
|
||||
const f3_2 = f3 * 2;
|
||||
const f4_2 = f4 * 2;
|
||||
const f5_2 = f5 * 2;
|
||||
const f6_2 = f6 * 2;
|
||||
const f7_2 = f7 * 2;
|
||||
const f5_38 = f5 * 38;
|
||||
const f6_19 = f6 * 19;
|
||||
const f7_38 = f7 * 38;
|
||||
const f8_19 = f8 * 19;
|
||||
const f9_38 = f9 * 38;
|
||||
|
||||
var t: [10]i64 = undefined;
|
||||
|
||||
t[0] = f0 * i64(f0) + f1_2 * i64(f9_38) + f2_2 * i64(f8_19) + f3_2 * i64(f7_38) + f4_2 * i64(f6_19) + f5 * i64(f5_38);
|
||||
t[1] = f0_2 * i64(f1) + f2 * i64(f9_38) + f3_2 * i64(f8_19) + f4 * i64(f7_38) + f5_2 * i64(f6_19);
|
||||
t[2] = f0_2 * i64(f2) + f1_2 * i64(f1) + f3_2 * i64(f9_38) + f4_2 * i64(f8_19) + f5_2 * i64(f7_38) + f6 * i64(f6_19);
|
||||
t[3] = f0_2 * i64(f3) + f1_2 * i64(f2) + f4 * i64(f9_38) + f5_2 * i64(f8_19) + f6 * i64(f7_38);
|
||||
t[4] = f0_2 * i64(f4) + f1_2 * i64(f3_2) + f2 * i64(f2) + f5_2 * i64(f9_38) + f6_2 * i64(f8_19) + f7 * i64(f7_38);
|
||||
t[5] = f0_2 * i64(f5) + f1_2 * i64(f4) + f2_2 * i64(f3) + f6 * i64(f9_38) + f7_2 * i64(f8_19);
|
||||
t[6] = f0_2 * i64(f6) + f1_2 * i64(f5_2) + f2_2 * i64(f4) + f3_2 * i64(f3) + f7_2 * i64(f9_38) + f8 * i64(f8_19);
|
||||
t[7] = f0_2 * i64(f7) + f1_2 * i64(f6) + f2_2 * i64(f5) + f3_2 * i64(f4) + f8 * i64(f9_38);
|
||||
t[8] = f0_2 * i64(f8) + f1_2 * i64(f7_2) + f2_2 * i64(f6) + f3_2 * i64(f5_2) + f4 * i64(f4) + f9 * i64(f9_38);
|
||||
t[9] = f0_2 * i64(f9) + f1_2 * i64(f8) + f2_2 * i64(f7) + f3_2 * i64(f6) + f4 * i64(f5_2);
|
||||
|
||||
carry2(h, t[0..]);
|
||||
}
|
||||
|
||||
fn sq2(h: *Fe, f: *const Fe) void {
|
||||
Fe.sq(h, f);
|
||||
Fe.mul_small(h, h, 2);
|
||||
}
|
||||
|
||||
// This could be simplified, but it would be slower
|
||||
fn invert(out: *Fe, z: *const Fe) void {
|
||||
var i: usize = undefined;
|
||||
|
||||
var t: [4]Fe = undefined;
|
||||
var t0 = &t[0];
|
||||
var t1 = &t[1];
|
||||
var t2 = &t[2];
|
||||
var t3 = &t[3];
|
||||
|
||||
Fe.sq(t0, z);
|
||||
Fe.sq(t1, t0);
|
||||
Fe.sq(t1, t1);
|
||||
Fe.mul(t1, z, t1);
|
||||
Fe.mul(t0, t0, t1);
|
||||
|
||||
Fe.sq(t2, t0);
|
||||
Fe.mul(t1, t1, t2);
|
||||
|
||||
Fe.sq(t2, t1);
|
||||
i = 1;
|
||||
while (i < 5) : (i += 1) Fe.sq(t2, t2);
|
||||
Fe.mul(t1, t2, t1);
|
||||
|
||||
Fe.sq(t2, t1);
|
||||
i = 1;
|
||||
while (i < 10) : (i += 1) Fe.sq(t2, t2);
|
||||
Fe.mul(t2, t2, t1);
|
||||
|
||||
Fe.sq(t3, t2);
|
||||
i = 1;
|
||||
while (i < 20) : (i += 1) Fe.sq(t3, t3);
|
||||
Fe.mul(t2, t3, t2);
|
||||
|
||||
Fe.sq(t2, t2);
|
||||
i = 1;
|
||||
while (i < 10) : (i += 1) Fe.sq(t2, t2);
|
||||
Fe.mul(t1, t2, t1);
|
||||
|
||||
Fe.sq(t2, t1);
|
||||
i = 1;
|
||||
while (i < 50) : (i += 1) Fe.sq(t2, t2);
|
||||
Fe.mul(t2, t2, t1);
|
||||
|
||||
Fe.sq(t3, t2);
|
||||
i = 1;
|
||||
while (i < 100) : (i += 1) Fe.sq(t3, t3);
|
||||
Fe.mul(t2, t3, t2);
|
||||
|
||||
Fe.sq(t2, t2);
|
||||
i = 1;
|
||||
while (i < 50) : (i += 1) Fe.sq(t2, t2);
|
||||
Fe.mul(t1, t2, t1);
|
||||
|
||||
Fe.sq(t1, t1);
|
||||
i = 1;
|
||||
while (i < 5) : (i += 1) Fe.sq(t1, t1);
|
||||
Fe.mul(out, t1, t0);
|
||||
|
||||
t0.secureZero();
|
||||
t1.secureZero();
|
||||
t2.secureZero();
|
||||
t3.secureZero();
|
||||
}
|
||||
|
||||
// This could be simplified, but it would be slower
|
||||
fn pow22523(out: *Fe, z: *const Fe) void {
|
||||
var i: usize = undefined;
|
||||
|
||||
var t: [3]Fe = undefined;
|
||||
var t0 = &t[0];
|
||||
var t1 = &t[1];
|
||||
var t2 = &t[2];
|
||||
|
||||
Fe.sq(t0, z);
|
||||
Fe.sq(t1, t0);
|
||||
Fe.sq(t1, t1);
|
||||
Fe.mul(t1, z, t1);
|
||||
Fe.mul(t0, t0, t1);
|
||||
|
||||
Fe.sq(t0, t0);
|
||||
Fe.mul(t0, t1, t0);
|
||||
|
||||
Fe.sq(t1, t0);
|
||||
i = 1;
|
||||
while (i < 5) : (i += 1) Fe.sq(t1, t1);
|
||||
Fe.mul(t0, t1, t0);
|
||||
|
||||
Fe.sq(t1, t0);
|
||||
i = 1;
|
||||
while (i < 10) : (i += 1) Fe.sq(t1, t1);
|
||||
Fe.mul(t1, t1, t0);
|
||||
|
||||
Fe.sq(t2, t1);
|
||||
i = 1;
|
||||
while (i < 20) : (i += 1) Fe.sq(t2, t2);
|
||||
Fe.mul(t1, t2, t1);
|
||||
|
||||
Fe.sq(t1, t1);
|
||||
i = 1;
|
||||
while (i < 10) : (i += 1) Fe.sq(t1, t1);
|
||||
Fe.mul(t0, t1, t0);
|
||||
|
||||
Fe.sq(t1, t0);
|
||||
i = 1;
|
||||
while (i < 50) : (i += 1) Fe.sq(t1, t1);
|
||||
Fe.mul(t1, t1, t0);
|
||||
|
||||
Fe.sq(t2, t1);
|
||||
i = 1;
|
||||
while (i < 100) : (i += 1) Fe.sq(t2, t2);
|
||||
Fe.mul(t1, t2, t1);
|
||||
|
||||
Fe.sq(t1, t1);
|
||||
i = 1;
|
||||
while (i < 50) : (i += 1) Fe.sq(t1, t1);
|
||||
Fe.mul(t0, t1, t0);
|
||||
|
||||
Fe.sq(t0, t0);
|
||||
i = 1;
|
||||
while (i < 2) : (i += 1) Fe.sq(t0, t0);
|
||||
Fe.mul(out, t0, z);
|
||||
|
||||
t0.secureZero();
|
||||
t1.secureZero();
|
||||
t2.secureZero();
|
||||
}
|
||||
|
||||
inline fn toBytesRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int) void {
|
||||
c[i] = t[i] >> shift;
|
||||
if (i + 1 < 10) {
|
||||
t[i + 1] += c[i];
|
||||
}
|
||||
t[i] -= c[i] * (i32(1) << shift);
|
||||
}
|
||||
|
||||
fn toBytes(s: []u8, h: *const Fe) void {
|
||||
std.debug.assert(s.len >= 32);
|
||||
|
||||
var t: [10]i64 = undefined;
|
||||
for (h.b[0..]) |_, i| {
|
||||
t[i] = h.b[i];
|
||||
}
|
||||
|
||||
var q = (19 * t[9] + ((i32(1) << 24))) >> 25;
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < 5) : (i += 1) {
|
||||
q += t[2 * i];
|
||||
q >>= 26;
|
||||
q += t[2 * i + 1];
|
||||
q >>= 25;
|
||||
}
|
||||
}
|
||||
t[0] += 19 * q;
|
||||
|
||||
var c: [10]i64 = undefined;
|
||||
|
||||
var st = t[0..];
|
||||
var sc = c[0..];
|
||||
|
||||
toBytesRound(sc, st, 0, 26);
|
||||
toBytesRound(sc, st, 1, 25);
|
||||
toBytesRound(sc, st, 2, 26);
|
||||
toBytesRound(sc, st, 3, 25);
|
||||
toBytesRound(sc, st, 4, 26);
|
||||
toBytesRound(sc, st, 5, 25);
|
||||
toBytesRound(sc, st, 6, 26);
|
||||
toBytesRound(sc, st, 7, 25);
|
||||
toBytesRound(sc, st, 8, 26);
|
||||
toBytesRound(sc, st, 9, 25);
|
||||
|
||||
var ut: [10]u32 = undefined;
|
||||
for (ut[0..]) |_, i| {
|
||||
ut[i] = @bitCast(u32, @intCast(i32, t[i]));
|
||||
}
|
||||
|
||||
writeInt(s[0..], (ut[0] >> 0) | (ut[1] << 26), Endian.Little);
|
||||
writeInt(s[4..], (ut[1] >> 6) | (ut[2] << 19), Endian.Little);
|
||||
writeInt(s[8..], (ut[2] >> 13) | (ut[3] << 13), Endian.Little);
|
||||
writeInt(s[12..], (ut[3] >> 19) | (ut[4] << 6), Endian.Little);
|
||||
writeInt(s[16..], (ut[5] >> 0) | (ut[6] << 25), Endian.Little);
|
||||
writeInt(s[20..], (ut[6] >> 7) | (ut[7] << 19), Endian.Little);
|
||||
writeInt(s[24..], (ut[7] >> 13) | (ut[8] << 12), Endian.Little);
|
||||
writeInt(s[28..], (ut[8] >> 20) | (ut[9] << 6), Endian.Little);
|
||||
|
||||
std.mem.secureZero(i64, t[0..]);
|
||||
}
|
||||
|
||||
// Parity check. Returns 0 if even, 1 if odd
|
||||
fn isNegative(f: *const Fe) bool {
|
||||
var s: [32]u8 = undefined;
|
||||
Fe.toBytes(s[0..], f);
|
||||
const isneg = s[0] & 1;
|
||||
s.secureZero();
|
||||
return isneg;
|
||||
}
|
||||
|
||||
fn isNonZero(f: *const Fe) bool {
|
||||
var s: [32]u8 = undefined;
|
||||
Fe.toBytes(s[0..], f);
|
||||
const isnonzero = zerocmp(u8, s[0..]);
|
||||
s.secureZero();
|
||||
return isneg;
|
||||
}
|
||||
};
|
||||
|
||||
test "x25519 rfc7748 vector1" {
|
||||
const secret_key = "\xa5\x46\xe3\x6b\xf0\x52\x7c\x9d\x3b\x16\x15\x4b\x82\x46\x5e\xdd\x62\x14\x4c\x0a\xc1\xfc\x5a\x18\x50\x6a\x22\x44\xba\x44\x9a\xc4";
|
||||
const public_key = "\xe6\xdb\x68\x67\x58\x30\x30\xdb\x35\x94\xc1\xa4\x24\xb1\x5f\x7c\x72\x66\x24\xec\x26\xb3\x35\x3b\x10\xa9\x03\xa6\xd0\xab\x1c\x4c";
|
||||
|
||||
const expected_output = "\xc3\xda\x55\x37\x9d\xe9\xc6\x90\x8e\x94\xea\x4d\xf2\x8d\x08\x4f\x32\xec\xcf\x03\x49\x1c\x71\xf7\x54\xb4\x07\x55\x77\xa2\x85\x52";
|
||||
|
||||
var output: [32]u8 = undefined;
|
||||
|
||||
std.debug.assert(X25519.create(output[0..], secret_key, public_key));
|
||||
std.debug.assert(std.mem.eql(u8, output, expected_output));
|
||||
}
|
||||
|
||||
test "x25519 rfc7748 vector2" {
|
||||
const secret_key = "\x4b\x66\xe9\xd4\xd1\xb4\x67\x3c\x5a\xd2\x26\x91\x95\x7d\x6a\xf5\xc1\x1b\x64\x21\xe0\xea\x01\xd4\x2c\xa4\x16\x9e\x79\x18\xba\x0d";
|
||||
const public_key = "\xe5\x21\x0f\x12\x78\x68\x11\xd3\xf4\xb7\x95\x9d\x05\x38\xae\x2c\x31\xdb\xe7\x10\x6f\xc0\x3c\x3e\xfc\x4c\xd5\x49\xc7\x15\xa4\x93";
|
||||
|
||||
const expected_output = "\x95\xcb\xde\x94\x76\xe8\x90\x7d\x7a\xad\xe4\x5c\xb4\xb8\x73\xf8\x8b\x59\x5a\x68\x79\x9f\xa1\x52\xe6\xf8\xf7\x64\x7a\xac\x79\x57";
|
||||
|
||||
var output: [32]u8 = undefined;
|
||||
|
||||
std.debug.assert(X25519.create(output[0..], secret_key, public_key));
|
||||
std.debug.assert(std.mem.eql(u8, output, expected_output));
|
||||
}
|
||||
|
||||
test "x25519 rfc7748 one iteration" {
|
||||
const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
const expected_output = "\x42\x2c\x8e\x7a\x62\x27\xd7\xbc\xa1\x35\x0b\x3e\x2b\xb7\x27\x9f\x78\x97\xb8\x7b\xb6\x85\x4b\x78\x3c\x60\xe8\x03\x11\xae\x30\x79";
|
||||
|
||||
var k: [32]u8 = initial_value;
|
||||
var u: [32]u8 = initial_value;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < 1) : (i += 1) {
|
||||
var output: [32]u8 = undefined;
|
||||
std.debug.assert(X25519.create(output[0..], k, u));
|
||||
|
||||
std.mem.copy(u8, u[0..], k[0..]);
|
||||
std.mem.copy(u8, k[0..], output[0..]);
|
||||
}
|
||||
|
||||
std.debug.assert(std.mem.eql(u8, k[0..], expected_output));
|
||||
}
|
||||
|
||||
test "x25519 rfc7748 1,000 iterations" {
|
||||
// These iteration tests are slow so we always skip them. Results have been verified.
|
||||
if (true) {
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
|
||||
const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
const expected_output = "\x68\x4c\xf5\x9b\xa8\x33\x09\x55\x28\x00\xef\x56\x6f\x2f\x4d\x3c\x1c\x38\x87\xc4\x93\x60\xe3\x87\x5f\x2e\xb9\x4d\x99\x53\x2c\x51";
|
||||
|
||||
var k: [32]u8 = initial_value;
|
||||
var u: [32]u8 = initial_value;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < 1000) : (i += 1) {
|
||||
var output: [32]u8 = undefined;
|
||||
std.debug.assert(X25519.create(output[0..], k, u));
|
||||
|
||||
std.mem.copy(u8, u[0..], k[0..]);
|
||||
std.mem.copy(u8, k[0..], output[0..]);
|
||||
}
|
||||
|
||||
std.debug.assert(std.mem.eql(u8, k[0..], expected_output));
|
||||
}
|
||||
|
||||
test "x25519 rfc7748 1,000,000 iterations" {
|
||||
if (true) {
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
|
||||
const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
const expected_output = "\x7c\x39\x11\xe0\xab\x25\x86\xfd\x86\x44\x97\x29\x7e\x57\x5e\x6f\x3b\xc6\x01\xc0\x88\x3c\x30\xdf\x5f\x4d\xd2\xd2\x4f\x66\x54\x24";
|
||||
|
||||
var k: [32]u8 = initial_value;
|
||||
var u: [32]u8 = initial_value;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < 1000000) : (i += 1) {
|
||||
var output: [32]u8 = undefined;
|
||||
std.debug.assert(X25519.create(output[0..], k, u));
|
||||
|
||||
std.mem.copy(u8, u[0..], k[0..]);
|
||||
std.mem.copy(u8, k[0..], output[0..]);
|
||||
}
|
||||
|
||||
std.debug.assert(std.mem.eql(u8, k[0..], expected_output));
|
||||
}
|
||||
@ -4,8 +4,11 @@ const mem = std.mem;
|
||||
const io = std.io;
|
||||
const os = std.os;
|
||||
const elf = std.elf;
|
||||
const macho = std.macho;
|
||||
const DW = std.dwarf;
|
||||
const macho = std.macho;
|
||||
const coff = std.coff;
|
||||
const pdb = std.pdb;
|
||||
const windows = os.windows;
|
||||
const ArrayList = std.ArrayList;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
@ -17,6 +20,17 @@ pub const runtime_safety = switch (builtin.mode) {
|
||||
builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false,
|
||||
};
|
||||
|
||||
const Module = struct {
|
||||
mod_info: pdb.ModInfo,
|
||||
module_name: []u8,
|
||||
obj_file_name: []u8,
|
||||
|
||||
populated: bool,
|
||||
symbols: []u8,
|
||||
subsect_info: []u8,
|
||||
checksum_offset: ?usize,
|
||||
};
|
||||
|
||||
/// Tries to write to stderr, unbuffered, and ignores any error returned.
|
||||
/// Does not append a newline.
|
||||
var stderr_file: os.File = undefined;
|
||||
@ -37,7 +51,7 @@ pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {
|
||||
return st;
|
||||
} else {
|
||||
stderr_file = try io.getStdErr();
|
||||
stderr_file_out_stream = io.FileOutStream.init(&stderr_file);
|
||||
stderr_file_out_stream = io.FileOutStream.init(stderr_file);
|
||||
const st = &stderr_file_out_stream.stream;
|
||||
stderr_stream = st;
|
||||
return st;
|
||||
@ -70,7 +84,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
|
||||
stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
|
||||
return;
|
||||
};
|
||||
writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, wantTtyColor(), start_addr) catch |err| {
|
||||
writeCurrentStackTrace(stderr, debug_info, wantTtyColor(), start_addr) catch |err| {
|
||||
stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
|
||||
return;
|
||||
};
|
||||
@ -191,7 +205,11 @@ pub inline fn getReturnAddress(frame_count: usize) usize {
|
||||
return @intToPtr(*const usize, fp + @sizeOf(usize)).*;
|
||||
}
|
||||
|
||||
pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void {
|
||||
pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void {
|
||||
switch (builtin.os) {
|
||||
builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr),
|
||||
else => {},
|
||||
}
|
||||
const AddressState = union(enum) {
|
||||
NotLookingForStartAddress,
|
||||
LookingForStartAddress: usize,
|
||||
@ -224,18 +242,296 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeCurrentStackTraceWindows(out_stream: var, debug_info: *DebugInfo,
|
||||
tty_color: bool, start_addr: ?usize) !void
|
||||
{
|
||||
var addr_buf: [1024]usize = undefined;
|
||||
const casted_len = @intCast(u32, addr_buf.len); // TODO shouldn't need this cast
|
||||
const n = windows.RtlCaptureStackBackTrace(0, casted_len, @ptrCast(**c_void, &addr_buf), null);
|
||||
const addrs = addr_buf[0..n];
|
||||
var start_i: usize = if (start_addr) |saddr| blk: {
|
||||
for (addrs) |addr, i| {
|
||||
if (addr == saddr) break :blk i;
|
||||
}
|
||||
return;
|
||||
} else 0;
|
||||
for (addrs[start_i..]) |addr| {
|
||||
try printSourceAtAddress(debug_info, out_stream, addr, tty_color);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void {
|
||||
switch (builtin.os) {
|
||||
builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color),
|
||||
builtin.Os.linux => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color),
|
||||
builtin.Os.windows => {
|
||||
// TODO https://github.com/ziglang/zig/issues/721
|
||||
return error.UnsupportedOperatingSystem;
|
||||
},
|
||||
builtin.Os.windows => return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color),
|
||||
else => return error.UnsupportedOperatingSystem,
|
||||
}
|
||||
}
|
||||
|
||||
fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_address: usize, tty_color: bool) !void {
|
||||
const allocator = getDebugInfoAllocator();
|
||||
const base_address = os.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;
|
||||
coff_section = &di.coff.sections.toSlice()[sect_contrib.Section];
|
||||
|
||||
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
|
||||
if (tty_color) {
|
||||
try out_stream.print("???:?:?: ");
|
||||
setTtyColor(TtyColor.Dim);
|
||||
try out_stream.print("0x{x} in ??? (???)", relocated_address);
|
||||
setTtyColor(TtyColor.Reset);
|
||||
try out_stream.print("\n\n\n");
|
||||
} else {
|
||||
try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", relocated_address);
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
const mod = &di.modules[mod_index];
|
||||
try populateModule(di, mod);
|
||||
const obj_basename = os.path.basename(mod.obj_file_name);
|
||||
|
||||
var symbol_i: usize = 0;
|
||||
const symbol_name = 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) {
|
||||
pdb.SymbolKind.S_LPROC32 => {
|
||||
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([*]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: usize = 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 block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]);
|
||||
line_index += @sizeOf(pdb.LineBlockFragmentHeader);
|
||||
|
||||
const has_column = line_hdr.Flags.LF_HaveColumns;
|
||||
|
||||
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) {
|
||||
var line_i: usize = 0;
|
||||
const start_line_index = line_index;
|
||||
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 flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags);
|
||||
const vaddr_start = frag_vaddr_start + line_num_entry.Offset;
|
||||
const vaddr_end = if (flags.End == 0) frag_vaddr_end else vaddr_start + flags.End;
|
||||
if (relative_address >= vaddr_start and relative_address < vaddr_end) {
|
||||
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 = flags.Start;
|
||||
const column = if (has_column) blk: {
|
||||
line_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines;
|
||||
line_index += @sizeOf(pdb.ColumnNumberEntry) * line_i;
|
||||
const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[line_index]);
|
||||
break :blk col_num_entry.StartColumn;
|
||||
} else 0;
|
||||
break :subsections LineInfo{
|
||||
.allocator = allocator,
|
||||
.file_name = source_file_name,
|
||||
.line = line,
|
||||
.column = column,
|
||||
};
|
||||
}
|
||||
}
|
||||
break :subsections null;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
if (sect_offset > subsect_info.len)
|
||||
return error.InvalidDebugInfo;
|
||||
} else {
|
||||
break :subsections null;
|
||||
}
|
||||
};
|
||||
|
||||
if (tty_color) {
|
||||
setTtyColor(TtyColor.White);
|
||||
if (opt_line_info) |li| {
|
||||
try out_stream.print("{}:{}:{}", li.file_name, li.line, li.column);
|
||||
} else {
|
||||
try out_stream.print("???:?:?");
|
||||
}
|
||||
setTtyColor(TtyColor.Reset);
|
||||
try out_stream.print(": ");
|
||||
setTtyColor(TtyColor.Dim);
|
||||
try out_stream.print("0x{x} in {} ({})", relocated_address, symbol_name, obj_basename);
|
||||
setTtyColor(TtyColor.Reset);
|
||||
|
||||
if (opt_line_info) |line_info| {
|
||||
try out_stream.print("\n");
|
||||
if (printLineFromFile(out_stream, line_info)) {
|
||||
if (line_info.column == 0) {
|
||||
try out_stream.write("\n");
|
||||
} else {
|
||||
{
|
||||
var col_i: usize = 1;
|
||||
while (col_i < line_info.column) : (col_i += 1) {
|
||||
try out_stream.writeByte(' ');
|
||||
}
|
||||
}
|
||||
setTtyColor(TtyColor.Green);
|
||||
try out_stream.write("^");
|
||||
setTtyColor(TtyColor.Reset);
|
||||
try out_stream.write("\n");
|
||||
}
|
||||
} else |err| switch (err) {
|
||||
error.EndOfFile => {},
|
||||
else => return err,
|
||||
}
|
||||
} else {
|
||||
try out_stream.print("\n\n\n");
|
||||
}
|
||||
} else {
|
||||
if (opt_line_info) |li| {
|
||||
try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n\n\n", li.file_name, li.line, li.column, relocated_address, symbol_name, obj_basename);
|
||||
} else {
|
||||
try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", relocated_address, symbol_name, obj_basename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const TtyColor = enum{
|
||||
Red,
|
||||
Green,
|
||||
Cyan,
|
||||
White,
|
||||
Dim,
|
||||
Bold,
|
||||
Reset,
|
||||
};
|
||||
|
||||
/// TODO this is a special case hack right now. clean it up and maybe make it part of std.fmt
|
||||
fn setTtyColor(tty_color: TtyColor) void {
|
||||
const S = struct {
|
||||
var attrs: windows.WORD = undefined;
|
||||
var init_attrs = false;
|
||||
};
|
||||
if (!S.init_attrs) {
|
||||
S.init_attrs = true;
|
||||
var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
|
||||
// TODO handle error
|
||||
_ = windows.GetConsoleScreenBufferInfo(stderr_file.handle, &info);
|
||||
S.attrs = info.wAttributes;
|
||||
}
|
||||
|
||||
// TODO handle errors
|
||||
switch (tty_color) {
|
||||
TtyColor.Red => {
|
||||
_ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED|windows.FOREGROUND_INTENSITY);
|
||||
},
|
||||
TtyColor.Green => {
|
||||
_ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN|windows.FOREGROUND_INTENSITY);
|
||||
},
|
||||
TtyColor.Cyan => {
|
||||
_ = windows.SetConsoleTextAttribute(stderr_file.handle,
|
||||
windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE|windows.FOREGROUND_INTENSITY);
|
||||
},
|
||||
TtyColor.White, TtyColor.Bold => {
|
||||
_ = windows.SetConsoleTextAttribute(stderr_file.handle,
|
||||
windows.FOREGROUND_RED|windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE|windows.FOREGROUND_INTENSITY);
|
||||
},
|
||||
TtyColor.Dim => {
|
||||
_ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY);
|
||||
},
|
||||
TtyColor.Reset => {
|
||||
_ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn populateModule(di: *DebugInfo, mod: *Module) !void {
|
||||
if (mod.populated)
|
||||
return;
|
||||
const allocator = getDebugInfoAllocator();
|
||||
|
||||
if (mod.mod_info.C11ByteSize != 0)
|
||||
return error.InvalidDebugInfo;
|
||||
|
||||
if (mod.mod_info.C13ByteSize == 0)
|
||||
return error.MissingDebugInfo;
|
||||
|
||||
const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo;
|
||||
|
||||
const signature = try modi.stream.readIntLe(u32);
|
||||
if (signature != 4)
|
||||
return error.InvalidDebugInfo;
|
||||
|
||||
mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4);
|
||||
try modi.stream.readNoEof(mod.symbols);
|
||||
|
||||
mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize);
|
||||
try modi.stream.readNoEof(mod.subsect_info);
|
||||
|
||||
var sect_offset: usize = 0;
|
||||
var skip_len: usize = undefined;
|
||||
while (sect_offset != mod.subsect_info.len) : (sect_offset += skip_len) {
|
||||
const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &mod.subsect_info[sect_offset]);
|
||||
skip_len = subsect_hdr.Length;
|
||||
sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
|
||||
|
||||
switch (subsect_hdr.Kind) {
|
||||
pdb.DebugSubsectionKind.FileChecksums => {
|
||||
mod.checksum_offset = sect_offset;
|
||||
break;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
if (sect_offset > mod.subsect_info.len)
|
||||
return error.InvalidDebugInfo;
|
||||
}
|
||||
|
||||
mod.populated = true;
|
||||
}
|
||||
|
||||
fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {
|
||||
var min: usize = 0;
|
||||
var max: usize = symbols.len - 1; // Exclude sentinel.
|
||||
@ -372,14 +668,185 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo {
|
||||
switch (builtin.os) {
|
||||
builtin.Os.linux => return openSelfDebugInfoLinux(allocator),
|
||||
builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator),
|
||||
builtin.Os.windows => {
|
||||
// TODO: https://github.com/ziglang/zig/issues/721
|
||||
return error.UnsupportedOperatingSystem;
|
||||
},
|
||||
builtin.Os.windows => return openSelfDebugInfoWindows(allocator),
|
||||
else => return error.UnsupportedOperatingSystem,
|
||||
}
|
||||
}
|
||||
|
||||
fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
|
||||
const self_file = try os.openSelfExe();
|
||||
defer self_file.close();
|
||||
|
||||
const coff_obj = try allocator.createOne(coff.Coff);
|
||||
coff_obj.* = coff.Coff{
|
||||
.in_file = self_file,
|
||||
.allocator = allocator,
|
||||
.coff_header = undefined,
|
||||
.pe_header = undefined,
|
||||
.sections = undefined,
|
||||
.guid = undefined,
|
||||
.age = undefined,
|
||||
};
|
||||
|
||||
var di = DebugInfo{
|
||||
.coff = coff_obj,
|
||||
.pdb = undefined,
|
||||
.sect_contribs = undefined,
|
||||
.modules = undefined,
|
||||
};
|
||||
|
||||
try di.coff.loadHeader();
|
||||
|
||||
var path_buf: [windows.MAX_PATH]u8 = undefined;
|
||||
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);
|
||||
|
||||
try di.pdb.openFile(di.coff, path);
|
||||
|
||||
var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo;
|
||||
const version = try pdb_stream.stream.readIntLe(u32);
|
||||
const signature = try pdb_stream.stream.readIntLe(u32);
|
||||
const age = try pdb_stream.stream.readIntLe(u32);
|
||||
var guid: [16]u8 = undefined;
|
||||
try pdb_stream.stream.readNoEof(guid[0..]);
|
||||
if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age)
|
||||
return error.InvalidDebugInfo;
|
||||
// We validated the executable and pdb match.
|
||||
|
||||
const string_table_index = str_tab_index: {
|
||||
const name_bytes_len = try pdb_stream.stream.readIntLe(u32);
|
||||
const name_bytes = try allocator.alloc(u8, name_bytes_len);
|
||||
try pdb_stream.stream.readNoEof(name_bytes);
|
||||
|
||||
const HashTableHeader = packed struct {
|
||||
Size: u32,
|
||||
Capacity: u32,
|
||||
|
||||
fn maxLoad(cap: u32) u32 {
|
||||
return cap * 2 / 3 + 1;
|
||||
}
|
||||
};
|
||||
var hash_tbl_hdr: HashTableHeader = undefined;
|
||||
try pdb_stream.stream.readStruct(HashTableHeader, &hash_tbl_hdr);
|
||||
if (hash_tbl_hdr.Capacity == 0)
|
||||
return error.InvalidDebugInfo;
|
||||
|
||||
if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity))
|
||||
return error.InvalidDebugInfo;
|
||||
|
||||
const present = try readSparseBitVector(&pdb_stream.stream, allocator);
|
||||
if (present.len != hash_tbl_hdr.Size)
|
||||
return error.InvalidDebugInfo;
|
||||
const deleted = try readSparseBitVector(&pdb_stream.stream, allocator);
|
||||
|
||||
const Bucket = struct {
|
||||
first: u32,
|
||||
second: u32,
|
||||
};
|
||||
const bucket_list = try allocator.alloc(Bucket, present.len);
|
||||
for (present) |_| {
|
||||
const name_offset = try pdb_stream.stream.readIntLe(u32);
|
||||
const name_index = try pdb_stream.stream.readIntLe(u32);
|
||||
const name = mem.toSlice(u8, name_bytes.ptr + name_offset);
|
||||
if (mem.eql(u8, name, "/names")) {
|
||||
break :str_tab_index name_index;
|
||||
}
|
||||
}
|
||||
return error.MissingDebugInfo;
|
||||
};
|
||||
|
||||
di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo;
|
||||
di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo;
|
||||
|
||||
const dbi = di.pdb.dbi;
|
||||
|
||||
// Dbi Header
|
||||
var dbi_stream_header: pdb.DbiStreamHeader = undefined;
|
||||
try dbi.stream.readStruct(pdb.DbiStreamHeader, &dbi_stream_header);
|
||||
const mod_info_size = dbi_stream_header.ModInfoSize;
|
||||
const section_contrib_size = dbi_stream_header.SectionContributionSize;
|
||||
|
||||
var modules = ArrayList(Module).init(allocator);
|
||||
|
||||
// Module Info Substream
|
||||
var mod_info_offset: usize = 0;
|
||||
while (mod_info_offset != mod_info_size) {
|
||||
var mod_info: pdb.ModInfo = undefined;
|
||||
try dbi.stream.readStruct(pdb.ModInfo, &mod_info);
|
||||
var this_record_len: usize = @sizeOf(pdb.ModInfo);
|
||||
|
||||
const module_name = try dbi.readNullTermString(allocator);
|
||||
this_record_len += module_name.len + 1;
|
||||
|
||||
const obj_file_name = try dbi.readNullTermString(allocator);
|
||||
this_record_len += obj_file_name.len + 1;
|
||||
|
||||
const march_forward_bytes = this_record_len % 4;
|
||||
if (march_forward_bytes != 0) {
|
||||
try dbi.seekForward(march_forward_bytes);
|
||||
this_record_len += march_forward_bytes;
|
||||
}
|
||||
|
||||
try modules.append(Module{
|
||||
.mod_info = mod_info,
|
||||
.module_name = module_name,
|
||||
.obj_file_name = obj_file_name,
|
||||
|
||||
.populated = false,
|
||||
.symbols = undefined,
|
||||
.subsect_info = undefined,
|
||||
.checksum_offset = null,
|
||||
});
|
||||
|
||||
mod_info_offset += this_record_len;
|
||||
if (mod_info_offset > mod_info_size)
|
||||
return error.InvalidDebugInfo;
|
||||
}
|
||||
|
||||
di.modules = modules.toOwnedSlice();
|
||||
|
||||
// Section Contribution Substream
|
||||
var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator);
|
||||
var sect_cont_offset: usize = 0;
|
||||
if (section_contrib_size != 0) {
|
||||
const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLe(u32));
|
||||
if (ver != pdb.SectionContrSubstreamVersion.Ver60)
|
||||
return error.InvalidDebugInfo;
|
||||
sect_cont_offset += @sizeOf(u32);
|
||||
}
|
||||
while (sect_cont_offset != section_contrib_size) {
|
||||
const entry = try sect_contribs.addOne();
|
||||
try dbi.stream.readStruct(pdb.SectionContribEntry, entry);
|
||||
sect_cont_offset += @sizeOf(pdb.SectionContribEntry);
|
||||
|
||||
if (sect_cont_offset > section_contrib_size)
|
||||
return error.InvalidDebugInfo;
|
||||
}
|
||||
|
||||
di.sect_contribs = sect_contribs.toOwnedSlice();
|
||||
|
||||
return di;
|
||||
}
|
||||
|
||||
fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {
|
||||
const num_words = try stream.readIntLe(u32);
|
||||
var word_i: usize = 0;
|
||||
var list = ArrayList(usize).init(allocator);
|
||||
while (word_i != num_words) : (word_i += 1) {
|
||||
const word = try stream.readIntLe(u32);
|
||||
var bit_i: u5 = 0;
|
||||
while (true) : (bit_i += 1) {
|
||||
if (word & (u32(1) << bit_i) != 0) {
|
||||
try list.append(word_i * 32 + bit_i);
|
||||
}
|
||||
if (bit_i == @maxValue(u5)) break;
|
||||
}
|
||||
}
|
||||
return list.toOwnedSlice();
|
||||
}
|
||||
|
||||
fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {
|
||||
var di = DebugInfo{
|
||||
.self_exe_file = undefined,
|
||||
@ -395,7 +862,7 @@ fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {
|
||||
di.self_exe_file = try os.openSelfExe();
|
||||
errdefer di.self_exe_file.close();
|
||||
|
||||
try di.elf.openFile(allocator, &di.self_exe_file);
|
||||
try di.elf.openFile(allocator, di.self_exe_file);
|
||||
errdefer di.elf.close();
|
||||
|
||||
di.debug_info = (try di.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo;
|
||||
@ -578,7 +1045,13 @@ pub const DebugInfo = switch (builtin.os) {
|
||||
return self.ofiles.allocator;
|
||||
}
|
||||
},
|
||||
else => struct {
|
||||
builtin.Os.windows => struct {
|
||||
pdb: pdb.Pdb,
|
||||
coff: *coff.Coff,
|
||||
sect_contribs: []pdb.SectionContribEntry,
|
||||
modules: []Module,
|
||||
},
|
||||
builtin.Os.linux => struct {
|
||||
self_exe_file: os.File,
|
||||
elf: elf.Elf,
|
||||
debug_info: *elf.SectionHeader,
|
||||
@ -594,7 +1067,7 @@ pub const DebugInfo = switch (builtin.os) {
|
||||
}
|
||||
|
||||
pub fn readString(self: *DebugInfo) ![]u8 {
|
||||
var in_file_stream = io.FileInStream.init(&self.self_exe_file);
|
||||
var in_file_stream = io.FileInStream.init(self.self_exe_file);
|
||||
const in_stream = &in_file_stream.stream;
|
||||
return readStringRaw(self.allocator(), in_stream);
|
||||
}
|
||||
@ -604,6 +1077,7 @@ pub const DebugInfo = switch (builtin.os) {
|
||||
self.elf.close();
|
||||
}
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
};
|
||||
|
||||
const PcRange = struct {
|
||||
@ -929,7 +1403,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
|
||||
}
|
||||
|
||||
fn parseAbbrevTable(st: *DebugInfo) !AbbrevTable {
|
||||
const in_file = &st.self_exe_file;
|
||||
const in_file = st.self_exe_file;
|
||||
var in_file_stream = io.FileInStream.init(in_file);
|
||||
const in_stream = &in_file_stream.stream;
|
||||
var result = AbbrevTable.init(st.allocator());
|
||||
@ -980,7 +1454,7 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con
|
||||
}
|
||||
|
||||
fn parseDie(st: *DebugInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die {
|
||||
const in_file = &st.self_exe_file;
|
||||
const in_file = st.self_exe_file;
|
||||
var in_file_stream = io.FileInStream.init(in_file);
|
||||
const in_stream = &in_file_stream.stream;
|
||||
const abbrev_code = try readULeb128(in_stream);
|
||||
@ -1202,7 +1676,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
|
||||
fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, target_address: usize) !LineInfo {
|
||||
const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);
|
||||
|
||||
const in_file = &di.self_exe_file;
|
||||
const in_file = di.self_exe_file;
|
||||
const debug_line_end = di.debug_line.offset + di.debug_line.size;
|
||||
var this_offset = di.debug_line.offset;
|
||||
var this_index: usize = 0;
|
||||
@ -1382,7 +1856,7 @@ fn scanAllCompileUnits(st: *DebugInfo) !void {
|
||||
var this_unit_offset = st.debug_info.offset;
|
||||
var cu_index: usize = 0;
|
||||
|
||||
var in_file_stream = io.FileInStream.init(&st.self_exe_file);
|
||||
var in_file_stream = io.FileInStream.init(st.self_exe_file);
|
||||
const in_stream = &in_file_stream.stream;
|
||||
|
||||
while (this_unit_offset < debug_info_end) {
|
||||
@ -1448,7 +1922,7 @@ fn scanAllCompileUnits(st: *DebugInfo) !void {
|
||||
}
|
||||
|
||||
fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit {
|
||||
var in_file_stream = io.FileInStream.init(&st.self_exe_file);
|
||||
var in_file_stream = io.FileInStream.init(st.self_exe_file);
|
||||
const in_stream = &in_file_stream.stream;
|
||||
for (st.compile_unit_list.toSlice()) |*compile_unit| {
|
||||
if (compile_unit.pc_range) |range| {
|
||||
|
||||
@ -353,7 +353,7 @@ pub const SectionHeader = struct {
|
||||
};
|
||||
|
||||
pub const Elf = struct {
|
||||
in_file: *os.File,
|
||||
in_file: os.File,
|
||||
auto_close_stream: bool,
|
||||
is_64: bool,
|
||||
endian: builtin.Endian,
|
||||
@ -376,7 +376,7 @@ pub const Elf = struct {
|
||||
}
|
||||
|
||||
/// Call close when done.
|
||||
pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: *os.File) !void {
|
||||
pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: os.File) !void {
|
||||
elf.allocator = allocator;
|
||||
elf.in_file = file;
|
||||
elf.auto_close_stream = false;
|
||||
|
||||
@ -7,7 +7,7 @@ const AtomicOrder = builtin.AtomicOrder;
|
||||
const Loop = std.event.Loop;
|
||||
|
||||
/// Thread-safe async/await lock.
|
||||
/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
|
||||
/// coroutines which are waiting for the lock are suspended, and
|
||||
/// are resumed when the lock is released, in order.
|
||||
/// Allows only one actor to hold the lock.
|
||||
pub const Lock = struct {
|
||||
|
||||
@ -3,7 +3,7 @@ const Lock = std.event.Lock;
|
||||
const Loop = std.event.Loop;
|
||||
|
||||
/// Thread-safe async/await lock that protects one piece of data.
|
||||
/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
|
||||
/// coroutines which are waiting for the lock are suspended, and
|
||||
/// are resumed when the lock is released, in order.
|
||||
pub fn Locked(comptime T: type) type {
|
||||
return struct {
|
||||
|
||||
@ -7,7 +7,7 @@ const AtomicOrder = builtin.AtomicOrder;
|
||||
const Loop = std.event.Loop;
|
||||
|
||||
/// Thread-safe async/await lock.
|
||||
/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
|
||||
/// coroutines which are waiting for the lock are suspended, and
|
||||
/// are resumed when the lock is released, in order.
|
||||
/// Many readers can hold the lock at the same time; however locking for writing is exclusive.
|
||||
/// When a read lock is held, it will not be released until the reader queue is empty.
|
||||
|
||||
@ -3,7 +3,7 @@ const RwLock = std.event.RwLock;
|
||||
const Loop = std.event.Loop;
|
||||
|
||||
/// Thread-safe async/await RW lock that protects one piece of data.
|
||||
/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
|
||||
/// coroutines which are waiting for the lock are suspended, and
|
||||
/// are resumed when the lock is released, in order.
|
||||
pub fn RwLocked(comptime T: type) type {
|
||||
return struct {
|
||||
|
||||
@ -89,7 +89,7 @@ pub const Server = struct {
|
||||
error.ProcessFdQuotaExceeded => {
|
||||
errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);
|
||||
suspend {
|
||||
self.waiting_for_emfile_node = PromiseNode.init( @handle() );
|
||||
self.waiting_for_emfile_node = PromiseNode.init(@handle());
|
||||
std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
|
||||
}
|
||||
continue;
|
||||
@ -145,11 +145,11 @@ test "listen on a port, send bytes, receive bytes" {
|
||||
cancel @handle();
|
||||
}
|
||||
}
|
||||
async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void {
|
||||
async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: std.os.File) !void {
|
||||
const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/733
|
||||
var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733
|
||||
var socket = _socket; // TODO https://github.com/ziglang/zig/issues/733
|
||||
|
||||
var adapter = std.io.FileOutStream.init(&socket);
|
||||
var adapter = std.io.FileOutStream.init(socket);
|
||||
var stream = &adapter.stream;
|
||||
try stream.print("hello from server\n");
|
||||
}
|
||||
|
||||
@ -163,26 +163,47 @@ pub fn formatType(
|
||||
}
|
||||
break :cf false;
|
||||
};
|
||||
|
||||
if (has_cust_fmt) return value.format(fmt, context, Errors, output);
|
||||
|
||||
try output(context, @typeName(T));
|
||||
if (comptime @typeId(T) == builtin.TypeId.Enum) {
|
||||
try output(context, ".");
|
||||
try formatType(@tagName(value), "", context, Errors, output);
|
||||
return;
|
||||
switch (comptime @typeId(T)) {
|
||||
builtin.TypeId.Enum => {
|
||||
try output(context, ".");
|
||||
try formatType(@tagName(value), "", context, Errors, output);
|
||||
return;
|
||||
},
|
||||
builtin.TypeId.Struct => {
|
||||
comptime var field_i = 0;
|
||||
inline while (field_i < @memberCount(T)) : (field_i += 1) {
|
||||
if (field_i == 0) {
|
||||
try output(context, "{ .");
|
||||
} else {
|
||||
try output(context, ", .");
|
||||
}
|
||||
try output(context, @memberName(T, field_i));
|
||||
try output(context, " = ");
|
||||
try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output);
|
||||
}
|
||||
try output(context, " }");
|
||||
},
|
||||
builtin.TypeId.Union => {
|
||||
const info = @typeInfo(T).Union;
|
||||
if (info.tag_type) |UnionTagType| {
|
||||
try output(context, "{ .");
|
||||
try output(context, @tagName(UnionTagType(value)));
|
||||
try output(context, " = ");
|
||||
inline for (info.fields) |u_field| {
|
||||
if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) {
|
||||
try formatType(@field(value, u_field.name), "", context, Errors, output);
|
||||
}
|
||||
}
|
||||
try output(context, " }");
|
||||
} else {
|
||||
try format(context, Errors, output, "@{x}", @ptrToInt(&value));
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
comptime var field_i = 0;
|
||||
inline while (field_i < @memberCount(T)) : (field_i += 1) {
|
||||
if (field_i == 0) {
|
||||
try output(context, "{ .");
|
||||
} else {
|
||||
try output(context, ", .");
|
||||
}
|
||||
try output(context, @memberName(T, field_i));
|
||||
try output(context, " = ");
|
||||
try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output);
|
||||
}
|
||||
try output(context, " }");
|
||||
return;
|
||||
},
|
||||
builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) {
|
||||
@ -329,6 +350,11 @@ pub fn formatText(
|
||||
comptime var width = 0;
|
||||
if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
|
||||
return formatBuf(bytes, width, context, Errors, output);
|
||||
} else if ((fmt[0] == 'x') or (fmt[0] == 'X')) {
|
||||
for (bytes) |c| {
|
||||
try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output);
|
||||
}
|
||||
return;
|
||||
} else @compileError("Unknown format character: " ++ []u8{fmt[0]});
|
||||
}
|
||||
return output(context, bytes);
|
||||
@ -1194,6 +1220,70 @@ test "fmt.format" {
|
||||
try testFmt("point: (10.200,2.220)\n", "point: {}\n", value);
|
||||
try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value);
|
||||
}
|
||||
//struct format
|
||||
{
|
||||
const S = struct {
|
||||
a: u32,
|
||||
b: error,
|
||||
};
|
||||
|
||||
const inst = S{
|
||||
.a = 456,
|
||||
.b = error.Unused,
|
||||
};
|
||||
|
||||
try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst);
|
||||
}
|
||||
//union format
|
||||
{
|
||||
const TU = union(enum) {
|
||||
float: f32,
|
||||
int: u32,
|
||||
};
|
||||
|
||||
const UU = union {
|
||||
float: f32,
|
||||
int: u32,
|
||||
};
|
||||
|
||||
const EU = extern union {
|
||||
float: f32,
|
||||
int: u32,
|
||||
};
|
||||
|
||||
const tu_inst = TU{ .int = 123 };
|
||||
const uu_inst = UU{ .int = 456 };
|
||||
const eu_inst = EU{ .float = 321.123 };
|
||||
|
||||
try testFmt("TU{ .int = 123 }", "{}", tu_inst);
|
||||
|
||||
var buf: [100]u8 = undefined;
|
||||
const uu_result = try bufPrint(buf[0..], "{}", uu_inst);
|
||||
debug.assert(mem.eql(u8, uu_result[0..3], "UU@"));
|
||||
|
||||
const eu_result = try bufPrint(buf[0..], "{}", eu_inst);
|
||||
debug.assert(mem.eql(u8, uu_result[0..3], "EU@"));
|
||||
}
|
||||
//enum format
|
||||
{
|
||||
const E = enum {
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
};
|
||||
|
||||
const inst = E.Two;
|
||||
|
||||
try testFmt("E.Two", "{}", inst);
|
||||
}
|
||||
//print bytes as hex
|
||||
{
|
||||
const some_bytes = "\xCA\xFE\xBA\xBE";
|
||||
try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes);
|
||||
try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes);
|
||||
const bytes_with_zeros = "\x00\x0E\xBA\xBE";
|
||||
try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros);
|
||||
}
|
||||
}
|
||||
|
||||
fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void {
|
||||
@ -1241,3 +1331,22 @@ pub fn isWhiteSpace(byte: u8) bool {
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn hexToBytes(out: []u8, input: []const u8) !void {
|
||||
if (out.len * 2 < input.len)
|
||||
return error.InvalidLength;
|
||||
|
||||
var in_i: usize = 0;
|
||||
while (in_i != input.len) : (in_i += 2) {
|
||||
const hi = try charToDigit(input[in_i], 16);
|
||||
const lo = try charToDigit(input[in_i + 1], 16);
|
||||
out[in_i / 2] = (hi << 4) | lo;
|
||||
}
|
||||
}
|
||||
|
||||
test "fmt.hexToBytes" {
|
||||
const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706";
|
||||
var pb: [32]u8 = undefined;
|
||||
try hexToBytes(pb[0..], test_hex_str);
|
||||
try testFmt(test_hex_str, "{X}", pb);
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ pub const atomic = @import("atomic/index.zig");
|
||||
pub const base64 = @import("base64.zig");
|
||||
pub const build = @import("build.zig");
|
||||
pub const c = @import("c/index.zig");
|
||||
pub const coff = @import("coff.zig");
|
||||
pub const crypto = @import("crypto/index.zig");
|
||||
pub const cstr = @import("cstr.zig");
|
||||
pub const debug = @import("debug/index.zig");
|
||||
@ -33,6 +34,7 @@ pub const math = @import("math/index.zig");
|
||||
pub const mem = @import("mem.zig");
|
||||
pub const net = @import("net.zig");
|
||||
pub const os = @import("os/index.zig");
|
||||
pub const pdb = @import("pdb.zig");
|
||||
pub const rand = @import("rand/index.zig");
|
||||
pub const rb = @import("rb.zig");
|
||||
pub const sort = @import("sort.zig");
|
||||
@ -56,6 +58,7 @@ test "std" {
|
||||
_ = @import("base64.zig");
|
||||
_ = @import("build.zig");
|
||||
_ = @import("c/index.zig");
|
||||
_ = @import("coff.zig");
|
||||
_ = @import("crypto/index.zig");
|
||||
_ = @import("cstr.zig");
|
||||
_ = @import("debug/index.zig");
|
||||
@ -74,6 +77,7 @@ test "std" {
|
||||
_ = @import("heap.zig");
|
||||
_ = @import("os/index.zig");
|
||||
_ = @import("rand/index.zig");
|
||||
_ = @import("pdb.zig");
|
||||
_ = @import("sort.zig");
|
||||
_ = @import("unicode.zig");
|
||||
_ = @import("zig/index.zig");
|
||||
|
||||
16
std/io.zig
16
std/io.zig
@ -34,13 +34,13 @@ pub fn getStdIn() GetStdIoErrs!File {
|
||||
|
||||
/// Implementation of InStream trait for File
|
||||
pub const FileInStream = struct {
|
||||
file: *File,
|
||||
file: File,
|
||||
stream: Stream,
|
||||
|
||||
pub const Error = @typeOf(File.read).ReturnType.ErrorSet;
|
||||
pub const Stream = InStream(Error);
|
||||
|
||||
pub fn init(file: *File) FileInStream {
|
||||
pub fn init(file: File) FileInStream {
|
||||
return FileInStream{
|
||||
.file = file,
|
||||
.stream = Stream{ .readFn = readFn },
|
||||
@ -55,13 +55,13 @@ pub const FileInStream = struct {
|
||||
|
||||
/// Implementation of OutStream trait for File
|
||||
pub const FileOutStream = struct {
|
||||
file: *File,
|
||||
file: File,
|
||||
stream: Stream,
|
||||
|
||||
pub const Error = File.WriteError;
|
||||
pub const Stream = OutStream(Error);
|
||||
|
||||
pub fn init(file: *File) FileOutStream {
|
||||
pub fn init(file: File) FileOutStream {
|
||||
return FileOutStream{
|
||||
.file = file,
|
||||
.stream = Stream{ .writeFn = writeFn },
|
||||
@ -210,7 +210,7 @@ pub fn InStream(comptime ReadError: type) type {
|
||||
|
||||
pub fn readStruct(self: *Self, comptime T: type, ptr: *T) !void {
|
||||
// Only extern and packed structs have defined in-memory layout.
|
||||
assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
|
||||
comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
|
||||
return self.readNoEof(@sliceToBytes((*[1]T)(ptr)[0..]));
|
||||
}
|
||||
};
|
||||
@ -280,7 +280,7 @@ pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptim
|
||||
const buf = try allocator.alignedAlloc(u8, A, size);
|
||||
errdefer allocator.free(buf);
|
||||
|
||||
var adapter = FileInStream.init(&file);
|
||||
var adapter = FileInStream.init(file);
|
||||
try adapter.stream.readNoEof(buf[0..size]);
|
||||
return buf;
|
||||
}
|
||||
@ -592,7 +592,7 @@ pub const BufferedAtomicFile = struct {
|
||||
self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.File.default_mode);
|
||||
errdefer self.atomic_file.deinit();
|
||||
|
||||
self.file_stream = FileOutStream.init(&self.atomic_file.file);
|
||||
self.file_stream = FileOutStream.init(self.atomic_file.file);
|
||||
self.buffered_stream = BufferedOutStream(FileOutStream.Error).init(&self.file_stream.stream);
|
||||
return self;
|
||||
}
|
||||
@ -622,7 +622,7 @@ test "import io tests" {
|
||||
|
||||
pub fn readLine(buf: []u8) !usize {
|
||||
var stdin = getStdIn() catch return error.StdInUnavailable;
|
||||
var adapter = FileInStream.init(&stdin);
|
||||
var adapter = FileInStream.init(stdin);
|
||||
var stream = &adapter.stream;
|
||||
var index: usize = 0;
|
||||
while (true) {
|
||||
|
||||
@ -19,7 +19,7 @@ test "write a file, read it, then delete it" {
|
||||
var file = try os.File.openWrite(tmp_file_name);
|
||||
defer file.close();
|
||||
|
||||
var file_out_stream = io.FileOutStream.init(&file);
|
||||
var file_out_stream = io.FileOutStream.init(file);
|
||||
var buf_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream);
|
||||
const st = &buf_stream.stream;
|
||||
try st.print("begin");
|
||||
@ -35,7 +35,7 @@ test "write a file, read it, then delete it" {
|
||||
const expected_file_size = "begin".len + data.len + "end".len;
|
||||
assert(file_size == expected_file_size);
|
||||
|
||||
var file_in_stream = io.FileInStream.init(&file);
|
||||
var file_in_stream = io.FileInStream.init(file);
|
||||
var buf_stream = io.BufferedInStream(io.FileInStream.Error).init(&file_in_stream.stream);
|
||||
const st = &buf_stream.stream;
|
||||
const contents = try st.readAllAlloc(allocator, 2 * 1024);
|
||||
|
||||
732
std/macho.zig
732
std/macho.zig
@ -1,4 +1,3 @@
|
||||
|
||||
pub const mach_header = extern struct {
|
||||
magic: u32,
|
||||
cputype: cpu_type_t,
|
||||
@ -25,26 +24,43 @@ pub const load_command = extern struct {
|
||||
cmdsize: u32,
|
||||
};
|
||||
|
||||
|
||||
/// 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
|
||||
/// <nlist.h> and <stab.h>.
|
||||
pub const symtab_command = extern struct {
|
||||
cmd: u32, /// LC_SYMTAB
|
||||
cmdsize: u32, /// sizeof(struct symtab_command)
|
||||
symoff: u32, /// symbol table offset
|
||||
nsyms: u32, /// number of symbol table entries
|
||||
stroff: u32, /// string table offset
|
||||
strsize: u32, /// string table size in bytes
|
||||
/// LC_SYMTAB
|
||||
cmd: u32,
|
||||
|
||||
/// sizeof(struct symtab_command)
|
||||
cmdsize: u32,
|
||||
|
||||
/// symbol table offset
|
||||
symoff: u32,
|
||||
|
||||
/// number of symbol table entries
|
||||
nsyms: u32,
|
||||
|
||||
/// string table offset
|
||||
stroff: u32,
|
||||
|
||||
/// string table size in bytes
|
||||
strsize: u32,
|
||||
};
|
||||
|
||||
/// The linkedit_data_command contains the offsets and sizes of a blob
|
||||
/// of data in the __LINKEDIT segment.
|
||||
/// of data in the __LINKEDIT segment.
|
||||
const linkedit_data_command = extern struct {
|
||||
cmd: u32,/// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT.
|
||||
cmdsize: u32, /// sizeof(struct linkedit_data_command)
|
||||
dataoff: u32 , /// file offset of data in __LINKEDIT segment
|
||||
datasize: u32 , /// file size of data in __LINKEDIT segment
|
||||
/// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT.
|
||||
cmd: u32,
|
||||
|
||||
/// sizeof(struct linkedit_data_command)
|
||||
cmdsize: u32,
|
||||
|
||||
/// file offset of data in __LINKEDIT segment
|
||||
dataoff: u32,
|
||||
|
||||
/// file size of data in __LINKEDIT segment
|
||||
datasize: u32,
|
||||
};
|
||||
|
||||
/// The segment load command indicates that a part of this file is to be
|
||||
@ -58,16 +74,35 @@ const linkedit_data_command = extern struct {
|
||||
/// section structures directly follow the segment command and their size is
|
||||
/// reflected in cmdsize.
|
||||
pub const segment_command = extern struct {
|
||||
cmd: u32,/// LC_SEGMENT
|
||||
cmdsize: u32,/// includes sizeof section structs
|
||||
segname: [16]u8,/// segment name
|
||||
vmaddr: u32,/// memory address of this segment
|
||||
vmsize: u32,/// memory size of this segment
|
||||
fileoff: u32,/// file offset of this segment
|
||||
filesize: u32,/// amount to map from the file
|
||||
maxprot: vm_prot_t,/// maximum VM protection
|
||||
initprot: vm_prot_t,/// initial VM protection
|
||||
nsects: u32,/// number of sections in segment
|
||||
/// LC_SEGMENT
|
||||
cmd: u32,
|
||||
|
||||
/// includes sizeof section structs
|
||||
cmdsize: u32,
|
||||
|
||||
/// segment name
|
||||
segname: [16]u8,
|
||||
|
||||
/// memory address of this segment
|
||||
vmaddr: u32,
|
||||
|
||||
/// memory size of this segment
|
||||
vmsize: u32,
|
||||
|
||||
/// file offset of this segment
|
||||
fileoff: u32,
|
||||
|
||||
/// amount to map from the file
|
||||
filesize: u32,
|
||||
|
||||
/// maximum VM protection
|
||||
maxprot: vm_prot_t,
|
||||
|
||||
/// initial VM protection
|
||||
initprot: vm_prot_t,
|
||||
|
||||
/// number of sections in segment
|
||||
nsects: u32,
|
||||
flags: u32,
|
||||
};
|
||||
|
||||
@ -76,17 +111,36 @@ pub const segment_command = extern struct {
|
||||
/// sections then section_64 structures directly follow the 64-bit segment
|
||||
/// command and their size is reflected in cmdsize.
|
||||
pub const segment_command_64 = extern struct {
|
||||
cmd: u32, /// LC_SEGMENT_64
|
||||
cmdsize: u32, /// includes sizeof section_64 structs
|
||||
segname: [16]u8, /// segment name
|
||||
vmaddr: u64, /// memory address of this segment
|
||||
vmsize: u64, /// memory size of this segment
|
||||
fileoff: u64, /// file offset of this segment
|
||||
filesize: u64, /// amount to map from the file
|
||||
maxprot: vm_prot_t, /// maximum VM protection
|
||||
initprot: vm_prot_t, /// initial VM protection
|
||||
nsects: u32, /// number of sections in segment
|
||||
flags: u32,
|
||||
/// LC_SEGMENT_64
|
||||
cmd: u32,
|
||||
|
||||
/// includes sizeof section_64 structs
|
||||
cmdsize: u32,
|
||||
|
||||
/// segment name
|
||||
segname: [16]u8,
|
||||
|
||||
/// memory address of this segment
|
||||
vmaddr: u64,
|
||||
|
||||
/// memory size of this segment
|
||||
vmsize: u64,
|
||||
|
||||
/// file offset of this segment
|
||||
fileoff: u64,
|
||||
|
||||
/// amount to map from the file
|
||||
filesize: u64,
|
||||
|
||||
/// maximum VM protection
|
||||
maxprot: vm_prot_t,
|
||||
|
||||
/// initial VM protection
|
||||
initprot: vm_prot_t,
|
||||
|
||||
/// number of sections in segment
|
||||
nsects: u32,
|
||||
flags: u32,
|
||||
};
|
||||
|
||||
/// A segment is made up of zero or more sections. Non-MH_OBJECT files have
|
||||
@ -115,32 +169,76 @@ pub const segment_command_64 = extern struct {
|
||||
/// fields of the section structure for mach object files is described in the
|
||||
/// header file <reloc.h>.
|
||||
pub const @"section" = extern struct {
|
||||
sectname: [16]u8, /// name of this section
|
||||
segname: [16]u8, /// segment this section goes in
|
||||
addr: u32, /// memory address of this section
|
||||
size: u32, /// size in bytes of this section
|
||||
offset: u32, /// file offset of this section
|
||||
@"align": u32, /// section alignment (power of 2)
|
||||
reloff: u32, /// file offset of relocation entries
|
||||
nreloc: u32, /// number of relocation entries
|
||||
flags: u32, /// flags (section type and attributes
|
||||
reserved1: u32, /// reserved (for offset or index)
|
||||
reserved2: u32, /// reserved (for count or sizeof)
|
||||
/// name of this section
|
||||
sectname: [16]u8,
|
||||
|
||||
/// segment this section goes in
|
||||
segname: [16]u8,
|
||||
|
||||
/// memory address of this section
|
||||
addr: u32,
|
||||
|
||||
/// size in bytes of this section
|
||||
size: u32,
|
||||
|
||||
/// file offset of this section
|
||||
offset: u32,
|
||||
|
||||
/// section alignment (power of 2)
|
||||
@"align": u32,
|
||||
|
||||
/// file offset of relocation entries
|
||||
reloff: u32,
|
||||
|
||||
/// number of relocation entries
|
||||
nreloc: u32,
|
||||
|
||||
/// flags (section type and attributes
|
||||
flags: u32,
|
||||
|
||||
/// reserved (for offset or index)
|
||||
reserved1: u32,
|
||||
|
||||
/// reserved (for count or sizeof)
|
||||
reserved2: u32,
|
||||
};
|
||||
|
||||
pub const section_64 = extern struct {
|
||||
sectname: [16]u8, /// name of this section
|
||||
segname: [16]u8, /// segment this section goes in
|
||||
addr: u64, /// memory address of this section
|
||||
size: u64, /// size in bytes of this section
|
||||
offset: u32, /// file offset of this section
|
||||
@"align": u32, /// section alignment (power of 2)
|
||||
reloff: u32, /// file offset of relocation entries
|
||||
nreloc: u32, /// number of relocation entries
|
||||
flags: u32, /// flags (section type and attributes
|
||||
reserved1: u32, /// reserved (for offset or index)
|
||||
reserved2: u32, /// reserved (for count or sizeof)
|
||||
reserved3: u32, /// reserved
|
||||
/// name of this section
|
||||
sectname: [16]u8,
|
||||
|
||||
/// segment this section goes in
|
||||
segname: [16]u8,
|
||||
|
||||
/// memory address of this section
|
||||
addr: u64,
|
||||
|
||||
/// size in bytes of this section
|
||||
size: u64,
|
||||
|
||||
/// file offset of this section
|
||||
offset: u32,
|
||||
|
||||
/// section alignment (power of 2)
|
||||
@"align": u32,
|
||||
|
||||
/// file offset of relocation entries
|
||||
reloff: u32,
|
||||
|
||||
/// number of relocation entries
|
||||
nreloc: u32,
|
||||
|
||||
/// flags (section type and attributes
|
||||
flags: u32,
|
||||
|
||||
/// reserved (for offset or index)
|
||||
reserved1: u32,
|
||||
|
||||
/// reserved (for count or sizeof)
|
||||
reserved2: u32,
|
||||
|
||||
/// reserved
|
||||
reserved3: u32,
|
||||
};
|
||||
|
||||
pub const nlist = extern struct {
|
||||
@ -168,116 +266,287 @@ pub const nlist_64 = extern struct {
|
||||
/// simply be ignored.
|
||||
pub const LC_REQ_DYLD = 0x80000000;
|
||||
|
||||
pub const LC_SEGMENT = 0x1; /// segment of this file to be mapped
|
||||
pub const LC_SYMTAB = 0x2; /// link-edit stab symbol table info
|
||||
pub const LC_SYMSEG = 0x3; /// link-edit gdb symbol table info (obsolete)
|
||||
pub const LC_THREAD = 0x4; /// thread
|
||||
pub const LC_UNIXTHREAD = 0x5; /// unix thread (includes a stack)
|
||||
pub const LC_LOADFVMLIB = 0x6; /// load a specified fixed VM shared library
|
||||
pub const LC_IDFVMLIB = 0x7; /// fixed VM shared library identification
|
||||
pub const LC_IDENT = 0x8; /// object identification info (obsolete)
|
||||
pub const LC_FVMFILE = 0x9; /// fixed VM file inclusion (internal use)
|
||||
pub const LC_PREPAGE = 0xa; /// prepage command (internal use)
|
||||
pub const LC_DYSYMTAB = 0xb; /// dynamic link-edit symbol table info
|
||||
pub const LC_LOAD_DYLIB = 0xc; /// load a dynamically linked shared library
|
||||
pub const LC_ID_DYLIB = 0xd; /// dynamically linked shared lib ident
|
||||
pub const LC_LOAD_DYLINKER = 0xe; /// load a dynamic linker
|
||||
pub const LC_ID_DYLINKER = 0xf; /// dynamic linker identification
|
||||
pub const LC_PREBOUND_DYLIB = 0x10; /// modules prebound for a dynamically
|
||||
pub const LC_ROUTINES = 0x11; /// image routines
|
||||
pub const LC_SUB_FRAMEWORK = 0x12; /// sub framework
|
||||
pub const LC_SUB_UMBRELLA = 0x13; /// sub umbrella
|
||||
pub const LC_SUB_CLIENT = 0x14; /// sub client
|
||||
pub const LC_SUB_LIBRARY = 0x15; /// sub library
|
||||
pub const LC_TWOLEVEL_HINTS = 0x16; /// two-level namespace lookup hints
|
||||
pub const LC_PREBIND_CKSUM = 0x17; /// prebind checksum
|
||||
/// segment of this file to be mapped
|
||||
pub const LC_SEGMENT = 0x1;
|
||||
|
||||
/// link-edit stab symbol table info
|
||||
pub const LC_SYMTAB = 0x2;
|
||||
|
||||
/// link-edit gdb symbol table info (obsolete)
|
||||
pub const LC_SYMSEG = 0x3;
|
||||
|
||||
/// thread
|
||||
pub const LC_THREAD = 0x4;
|
||||
|
||||
/// unix thread (includes a stack)
|
||||
pub const LC_UNIXTHREAD = 0x5;
|
||||
|
||||
/// load a specified fixed VM shared library
|
||||
pub const LC_LOADFVMLIB = 0x6;
|
||||
|
||||
/// fixed VM shared library identification
|
||||
pub const LC_IDFVMLIB = 0x7;
|
||||
|
||||
/// object identification info (obsolete)
|
||||
pub const LC_IDENT = 0x8;
|
||||
|
||||
/// fixed VM file inclusion (internal use)
|
||||
pub const LC_FVMFILE = 0x9;
|
||||
|
||||
/// prepage command (internal use)
|
||||
pub const LC_PREPAGE = 0xa;
|
||||
|
||||
/// dynamic link-edit symbol table info
|
||||
pub const LC_DYSYMTAB = 0xb;
|
||||
|
||||
/// load a dynamically linked shared library
|
||||
pub const LC_LOAD_DYLIB = 0xc;
|
||||
|
||||
/// dynamically linked shared lib ident
|
||||
pub const LC_ID_DYLIB = 0xd;
|
||||
|
||||
/// load a dynamic linker
|
||||
pub const LC_LOAD_DYLINKER = 0xe;
|
||||
|
||||
/// dynamic linker identification
|
||||
pub const LC_ID_DYLINKER = 0xf;
|
||||
|
||||
/// modules prebound for a dynamically
|
||||
pub const LC_PREBOUND_DYLIB = 0x10;
|
||||
|
||||
/// image routines
|
||||
pub const LC_ROUTINES = 0x11;
|
||||
|
||||
/// sub framework
|
||||
pub const LC_SUB_FRAMEWORK = 0x12;
|
||||
|
||||
/// sub umbrella
|
||||
pub const LC_SUB_UMBRELLA = 0x13;
|
||||
|
||||
/// sub client
|
||||
pub const LC_SUB_CLIENT = 0x14;
|
||||
|
||||
/// sub library
|
||||
pub const LC_SUB_LIBRARY = 0x15;
|
||||
|
||||
/// two-level namespace lookup hints
|
||||
pub const LC_TWOLEVEL_HINTS = 0x16;
|
||||
|
||||
/// prebind checksum
|
||||
pub const LC_PREBIND_CKSUM = 0x17;
|
||||
|
||||
/// load a dynamically linked shared library that is allowed to be missing
|
||||
/// (all symbols are weak imported).
|
||||
pub const LC_LOAD_WEAK_DYLIB = (0x18 | LC_REQ_DYLD);
|
||||
|
||||
pub const LC_SEGMENT_64 = 0x19; /// 64-bit segment of this file to be mapped
|
||||
pub const LC_ROUTINES_64 = 0x1a; /// 64-bit image routines
|
||||
pub const LC_UUID = 0x1b; /// the uuid
|
||||
pub const LC_RPATH = (0x1c | LC_REQ_DYLD); /// runpath additions
|
||||
pub const LC_CODE_SIGNATURE = 0x1d; /// local of code signature
|
||||
pub const LC_SEGMENT_SPLIT_INFO = 0x1e; /// local of info to split segments
|
||||
pub const LC_REEXPORT_DYLIB = (0x1f | LC_REQ_DYLD); /// load and re-export dylib
|
||||
pub const LC_LAZY_LOAD_DYLIB = 0x20; /// delay load of dylib until first use
|
||||
pub const LC_ENCRYPTION_INFO = 0x21; /// encrypted segment information
|
||||
pub const LC_DYLD_INFO = 0x22; /// compressed dyld information
|
||||
pub const LC_DYLD_INFO_ONLY = (0x22|LC_REQ_DYLD); /// compressed dyld information only
|
||||
pub const LC_LOAD_UPWARD_DYLIB = (0x23 | LC_REQ_DYLD); /// load upward dylib
|
||||
pub const LC_VERSION_MIN_MACOSX = 0x24; /// build for MacOSX min OS version
|
||||
pub const LC_VERSION_MIN_IPHONEOS = 0x25; /// build for iPhoneOS min OS version
|
||||
pub const LC_FUNCTION_STARTS = 0x26; /// compressed table of function start addresses
|
||||
pub const LC_DYLD_ENVIRONMENT = 0x27; /// string for dyld to treat like environment variable
|
||||
pub const LC_MAIN = (0x28|LC_REQ_DYLD); /// replacement for LC_UNIXTHREAD
|
||||
pub const LC_DATA_IN_CODE = 0x29; /// table of non-instructions in __text
|
||||
pub const LC_SOURCE_VERSION = 0x2A; /// source version used to build binary
|
||||
pub const LC_DYLIB_CODE_SIGN_DRS = 0x2B; /// Code signing DRs copied from linked dylibs
|
||||
pub const LC_ENCRYPTION_INFO_64 = 0x2C; /// 64-bit encrypted segment information
|
||||
pub const LC_LINKER_OPTION = 0x2D; /// linker options in MH_OBJECT files
|
||||
pub const LC_LINKER_OPTIMIZATION_HINT = 0x2E; /// optimization hints in MH_OBJECT files
|
||||
pub const LC_VERSION_MIN_TVOS = 0x2F; /// build for AppleTV min OS version
|
||||
pub const LC_VERSION_MIN_WATCHOS = 0x30; /// build for Watch min OS version
|
||||
pub const LC_NOTE = 0x31; /// arbitrary data included within a Mach-O file
|
||||
pub const LC_BUILD_VERSION = 0x32; /// build for platform min OS version
|
||||
/// 64-bit segment of this file to be mapped
|
||||
pub const LC_SEGMENT_64 = 0x19;
|
||||
|
||||
pub const MH_MAGIC = 0xfeedface; /// the mach magic number
|
||||
pub const MH_CIGAM = 0xcefaedfe; /// NXSwapInt(MH_MAGIC)
|
||||
/// 64-bit image routines
|
||||
pub const LC_ROUTINES_64 = 0x1a;
|
||||
|
||||
pub const MH_MAGIC_64 = 0xfeedfacf; /// the 64-bit mach magic number
|
||||
pub const MH_CIGAM_64 = 0xcffaedfe; /// NXSwapInt(MH_MAGIC_64)
|
||||
/// the uuid
|
||||
pub const LC_UUID = 0x1b;
|
||||
|
||||
pub const MH_OBJECT = 0x1; /// relocatable object file
|
||||
pub const MH_EXECUTE = 0x2; /// demand paged executable file
|
||||
pub const MH_FVMLIB = 0x3; /// fixed VM shared library file
|
||||
pub const MH_CORE = 0x4; /// core file
|
||||
pub const MH_PRELOAD = 0x5; /// preloaded executable file
|
||||
pub const MH_DYLIB = 0x6; /// dynamically bound shared library
|
||||
pub const MH_DYLINKER = 0x7; /// dynamic link editor
|
||||
pub const MH_BUNDLE = 0x8; /// dynamically bound bundle file
|
||||
pub const MH_DYLIB_STUB = 0x9; /// shared library stub for static linking only, no section contents
|
||||
pub const MH_DSYM = 0xa; /// companion file with only debug sections
|
||||
pub const MH_KEXT_BUNDLE = 0xb; /// x86_64 kexts
|
||||
/// runpath additions
|
||||
pub const LC_RPATH = (0x1c | LC_REQ_DYLD);
|
||||
|
||||
/// local of code signature
|
||||
pub const LC_CODE_SIGNATURE = 0x1d;
|
||||
|
||||
/// local of info to split segments
|
||||
pub const LC_SEGMENT_SPLIT_INFO = 0x1e;
|
||||
|
||||
/// load and re-export dylib
|
||||
pub const LC_REEXPORT_DYLIB = (0x1f | LC_REQ_DYLD);
|
||||
|
||||
/// delay load of dylib until first use
|
||||
pub const LC_LAZY_LOAD_DYLIB = 0x20;
|
||||
|
||||
/// encrypted segment information
|
||||
pub const LC_ENCRYPTION_INFO = 0x21;
|
||||
|
||||
/// compressed dyld information
|
||||
pub const LC_DYLD_INFO = 0x22;
|
||||
|
||||
/// compressed dyld information only
|
||||
pub const LC_DYLD_INFO_ONLY = (0x22 | LC_REQ_DYLD);
|
||||
|
||||
/// load upward dylib
|
||||
pub const LC_LOAD_UPWARD_DYLIB = (0x23 | LC_REQ_DYLD);
|
||||
|
||||
/// build for MacOSX min OS version
|
||||
pub const LC_VERSION_MIN_MACOSX = 0x24;
|
||||
|
||||
/// build for iPhoneOS min OS version
|
||||
pub const LC_VERSION_MIN_IPHONEOS = 0x25;
|
||||
|
||||
/// compressed table of function start addresses
|
||||
pub const LC_FUNCTION_STARTS = 0x26;
|
||||
|
||||
/// string for dyld to treat like environment variable
|
||||
pub const LC_DYLD_ENVIRONMENT = 0x27;
|
||||
|
||||
/// replacement for LC_UNIXTHREAD
|
||||
pub const LC_MAIN = (0x28 | LC_REQ_DYLD);
|
||||
|
||||
/// table of non-instructions in __text
|
||||
pub const LC_DATA_IN_CODE = 0x29;
|
||||
|
||||
/// source version used to build binary
|
||||
pub const LC_SOURCE_VERSION = 0x2A;
|
||||
|
||||
/// Code signing DRs copied from linked dylibs
|
||||
pub const LC_DYLIB_CODE_SIGN_DRS = 0x2B;
|
||||
|
||||
/// 64-bit encrypted segment information
|
||||
pub const LC_ENCRYPTION_INFO_64 = 0x2C;
|
||||
|
||||
/// linker options in MH_OBJECT files
|
||||
pub const LC_LINKER_OPTION = 0x2D;
|
||||
|
||||
/// optimization hints in MH_OBJECT files
|
||||
pub const LC_LINKER_OPTIMIZATION_HINT = 0x2E;
|
||||
|
||||
/// build for AppleTV min OS version
|
||||
pub const LC_VERSION_MIN_TVOS = 0x2F;
|
||||
|
||||
/// build for Watch min OS version
|
||||
pub const LC_VERSION_MIN_WATCHOS = 0x30;
|
||||
|
||||
/// arbitrary data included within a Mach-O file
|
||||
pub const LC_NOTE = 0x31;
|
||||
|
||||
/// build for platform min OS version
|
||||
pub const LC_BUILD_VERSION = 0x32;
|
||||
|
||||
/// the mach magic number
|
||||
pub const MH_MAGIC = 0xfeedface;
|
||||
|
||||
/// NXSwapInt(MH_MAGIC)
|
||||
pub const MH_CIGAM = 0xcefaedfe;
|
||||
|
||||
/// the 64-bit mach magic number
|
||||
pub const MH_MAGIC_64 = 0xfeedfacf;
|
||||
|
||||
/// NXSwapInt(MH_MAGIC_64)
|
||||
pub const MH_CIGAM_64 = 0xcffaedfe;
|
||||
|
||||
/// relocatable object file
|
||||
pub const MH_OBJECT = 0x1;
|
||||
|
||||
/// demand paged executable file
|
||||
pub const MH_EXECUTE = 0x2;
|
||||
|
||||
/// fixed VM shared library file
|
||||
pub const MH_FVMLIB = 0x3;
|
||||
|
||||
/// core file
|
||||
pub const MH_CORE = 0x4;
|
||||
|
||||
/// preloaded executable file
|
||||
pub const MH_PRELOAD = 0x5;
|
||||
|
||||
/// dynamically bound shared library
|
||||
pub const MH_DYLIB = 0x6;
|
||||
|
||||
/// dynamic link editor
|
||||
pub const MH_DYLINKER = 0x7;
|
||||
|
||||
/// dynamically bound bundle file
|
||||
pub const MH_BUNDLE = 0x8;
|
||||
|
||||
/// shared library stub for static linking only, no section contents
|
||||
pub const MH_DYLIB_STUB = 0x9;
|
||||
|
||||
/// companion file with only debug sections
|
||||
pub const MH_DSYM = 0xa;
|
||||
|
||||
/// x86_64 kexts
|
||||
pub const MH_KEXT_BUNDLE = 0xb;
|
||||
|
||||
// Constants for the flags field of the mach_header
|
||||
|
||||
pub const MH_NOUNDEFS = 0x1; /// the object file has no undefined references
|
||||
pub const MH_INCRLINK = 0x2; /// the object file is the output of an incremental link against a base file and can't be link edited again
|
||||
pub const MH_DYLDLINK = 0x4; /// the object file is input for the dynamic linker and can't be staticly link edited again
|
||||
pub const MH_BINDATLOAD = 0x8; /// the object file's undefined references are bound by the dynamic linker when loaded.
|
||||
pub const MH_PREBOUND = 0x10; /// the file has its dynamic undefined references prebound.
|
||||
pub const MH_SPLIT_SEGS = 0x20; /// the file has its read-only and read-write segments split
|
||||
pub const MH_LAZY_INIT = 0x40; /// the shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete)
|
||||
pub const MH_TWOLEVEL = 0x80; /// the image is using two-level name space bindings
|
||||
pub const MH_FORCE_FLAT = 0x100; /// the executable is forcing all images to use flat name space bindings
|
||||
pub const MH_NOMULTIDEFS = 0x200; /// this umbrella guarantees no multiple defintions of symbols in its sub-images so the two-level namespace hints can always be used.
|
||||
pub const MH_NOFIXPREBINDING = 0x400; /// do not have dyld notify the prebinding agent about this executable
|
||||
pub const MH_PREBINDABLE = 0x800; /// the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set.
|
||||
pub const MH_ALLMODSBOUND = 0x1000; /// indicates that this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set.
|
||||
pub const MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000;/// safe to divide up the sections into sub-sections via symbols for dead code stripping
|
||||
pub const MH_CANONICAL = 0x4000; /// the binary has been canonicalized via the unprebind operation
|
||||
pub const MH_WEAK_DEFINES = 0x8000; /// the final linked image contains external weak symbols
|
||||
pub const MH_BINDS_TO_WEAK = 0x10000; /// the final linked image uses weak symbols
|
||||
/// the object file has no undefined references
|
||||
pub const MH_NOUNDEFS = 0x1;
|
||||
|
||||
pub const MH_ALLOW_STACK_EXECUTION = 0x20000;/// When this bit is set, all stacks in the task will be given stack execution privilege. Only used in MH_EXECUTE filetypes.
|
||||
pub const MH_ROOT_SAFE = 0x40000; /// When this bit is set, the binary declares it is safe for use in processes with uid zero
|
||||
|
||||
pub const MH_SETUID_SAFE = 0x80000; /// When this bit is set, the binary declares it is safe for use in processes when issetugid() is true
|
||||
/// the object file is the output of an incremental link against a base file and can't be link edited again
|
||||
pub const MH_INCRLINK = 0x2;
|
||||
|
||||
pub const MH_NO_REEXPORTED_DYLIBS = 0x100000; /// When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported
|
||||
pub const MH_PIE = 0x200000; /// When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes.
|
||||
pub const MH_DEAD_STRIPPABLE_DYLIB = 0x400000; /// Only for use on dylibs. When linking against a dylib that has this bit set, the static linker will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are being referenced from the dylib.
|
||||
pub const MH_HAS_TLV_DESCRIPTORS = 0x800000; /// Contains a section of type S_THREAD_LOCAL_VARIABLES
|
||||
/// the object file is input for the dynamic linker and can't be staticly link edited again
|
||||
pub const MH_DYLDLINK = 0x4;
|
||||
|
||||
pub const MH_NO_HEAP_EXECUTION = 0x1000000; /// When this bit is set, the OS will run the main executable with a non-executable heap even on platforms (e.g. i386) that don't require it. Only used in MH_EXECUTE filetypes.
|
||||
/// the object file's undefined references are bound by the dynamic linker when loaded.
|
||||
pub const MH_BINDATLOAD = 0x8;
|
||||
|
||||
pub const MH_APP_EXTENSION_SAFE = 0x02000000; /// The code was linked for use in an application extension.
|
||||
/// the file has its dynamic undefined references prebound.
|
||||
pub const MH_PREBOUND = 0x10;
|
||||
|
||||
pub const MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000; /// The external symbols listed in the nlist symbol table do not include all the symbols listed in the dyld info.
|
||||
/// the file has its read-only and read-write segments split
|
||||
pub const MH_SPLIT_SEGS = 0x20;
|
||||
|
||||
/// the shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete)
|
||||
pub const MH_LAZY_INIT = 0x40;
|
||||
|
||||
/// the image is using two-level name space bindings
|
||||
pub const MH_TWOLEVEL = 0x80;
|
||||
|
||||
/// the executable is forcing all images to use flat name space bindings
|
||||
pub const MH_FORCE_FLAT = 0x100;
|
||||
|
||||
/// this umbrella guarantees no multiple defintions of symbols in its sub-images so the two-level namespace hints can always be used.
|
||||
pub const MH_NOMULTIDEFS = 0x200;
|
||||
|
||||
/// do not have dyld notify the prebinding agent about this executable
|
||||
pub const MH_NOFIXPREBINDING = 0x400;
|
||||
|
||||
/// the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set.
|
||||
pub const MH_PREBINDABLE = 0x800;
|
||||
|
||||
/// indicates that this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set.
|
||||
pub const MH_ALLMODSBOUND = 0x1000;
|
||||
|
||||
/// safe to divide up the sections into sub-sections via symbols for dead code stripping
|
||||
pub const MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000;
|
||||
|
||||
/// the binary has been canonicalized via the unprebind operation
|
||||
pub const MH_CANONICAL = 0x4000;
|
||||
|
||||
/// the final linked image contains external weak symbols
|
||||
pub const MH_WEAK_DEFINES = 0x8000;
|
||||
|
||||
/// the final linked image uses weak symbols
|
||||
pub const MH_BINDS_TO_WEAK = 0x10000;
|
||||
|
||||
/// When this bit is set, all stacks in the task will be given stack execution privilege. Only used in MH_EXECUTE filetypes.
|
||||
pub const MH_ALLOW_STACK_EXECUTION = 0x20000;
|
||||
|
||||
/// When this bit is set, the binary declares it is safe for use in processes with uid zero
|
||||
pub const MH_ROOT_SAFE = 0x40000;
|
||||
|
||||
/// When this bit is set, the binary declares it is safe for use in processes when issetugid() is true
|
||||
pub const MH_SETUID_SAFE = 0x80000;
|
||||
|
||||
/// When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported
|
||||
pub const MH_NO_REEXPORTED_DYLIBS = 0x100000;
|
||||
|
||||
/// When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes.
|
||||
pub const MH_PIE = 0x200000;
|
||||
|
||||
/// Only for use on dylibs. When linking against a dylib that has this bit set, the static linker will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are being referenced from the dylib.
|
||||
pub const MH_DEAD_STRIPPABLE_DYLIB = 0x400000;
|
||||
|
||||
/// Contains a section of type S_THREAD_LOCAL_VARIABLES
|
||||
pub const MH_HAS_TLV_DESCRIPTORS = 0x800000;
|
||||
|
||||
/// When this bit is set, the OS will run the main executable with a non-executable heap even on platforms (e.g. i386) that don't require it. Only used in MH_EXECUTE filetypes.
|
||||
pub const MH_NO_HEAP_EXECUTION = 0x1000000;
|
||||
|
||||
/// The code was linked for use in an application extension.
|
||||
pub const MH_APP_EXTENSION_SAFE = 0x02000000;
|
||||
|
||||
/// The external symbols listed in the nlist symbol table do not include all the symbols listed in the dyld info.
|
||||
pub const MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000;
|
||||
|
||||
/// The flags field of a section structure is separated into two parts a section
|
||||
/// type and section attributes. The section types are mutually exclusive (it
|
||||
@ -285,52 +554,129 @@ pub const MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000; /// The external symbol
|
||||
/// than one attribute).
|
||||
/// 256 section types
|
||||
pub const SECTION_TYPE = 0x000000ff;
|
||||
pub const SECTION_ATTRIBUTES = 0xffffff00; /// 24 section attributes
|
||||
|
||||
pub const S_REGULAR = 0x0; /// regular section
|
||||
pub const S_ZEROFILL = 0x1; /// zero fill on demand section
|
||||
pub const S_CSTRING_LITERALS = 0x2; /// section with only literal C string
|
||||
pub const S_4BYTE_LITERALS = 0x3; /// section with only 4 byte literals
|
||||
pub const S_8BYTE_LITERALS = 0x4; /// section with only 8 byte literals
|
||||
pub const S_LITERAL_POINTERS = 0x5; /// section with only pointers to
|
||||
/// 24 section attributes
|
||||
pub const SECTION_ATTRIBUTES = 0xffffff00;
|
||||
|
||||
/// regular section
|
||||
pub const S_REGULAR = 0x0;
|
||||
|
||||
pub const N_STAB = 0xe0; /// if any of these bits set, a symbolic debugging entry
|
||||
pub const N_PEXT = 0x10; /// private external symbol bit
|
||||
pub const N_TYPE = 0x0e; /// mask for the type bits
|
||||
pub const N_EXT = 0x01; /// external symbol bit, set for external symbols
|
||||
/// zero fill on demand section
|
||||
pub const S_ZEROFILL = 0x1;
|
||||
|
||||
/// section with only literal C string
|
||||
pub const S_CSTRING_LITERALS = 0x2;
|
||||
|
||||
pub const N_GSYM = 0x20; /// global symbol: name,,NO_SECT,type,0
|
||||
pub const N_FNAME = 0x22; /// procedure name (f77 kludge): name,,NO_SECT,0,0
|
||||
pub const N_FUN = 0x24; /// procedure: name,,n_sect,linenumber,address
|
||||
pub const N_STSYM = 0x26; /// static symbol: name,,n_sect,type,address
|
||||
pub const N_LCSYM = 0x28; /// .lcomm symbol: name,,n_sect,type,address
|
||||
pub const N_BNSYM = 0x2e; /// begin nsect sym: 0,,n_sect,0,address
|
||||
pub const N_AST = 0x32; /// AST file path: name,,NO_SECT,0,0
|
||||
pub const N_OPT = 0x3c; /// emitted with gcc2_compiled and in gcc source
|
||||
pub const N_RSYM = 0x40; /// register sym: name,,NO_SECT,type,register
|
||||
pub const N_SLINE = 0x44; /// src line: 0,,n_sect,linenumber,address
|
||||
pub const N_ENSYM = 0x4e; /// end nsect sym: 0,,n_sect,0,address
|
||||
pub const N_SSYM = 0x60; /// structure elt: name,,NO_SECT,type,struct_offset
|
||||
pub const N_SO = 0x64; /// source file name: name,,n_sect,0,address
|
||||
pub const N_OSO = 0x66; /// object file name: name,,0,0,st_mtime
|
||||
pub const N_LSYM = 0x80; /// local sym: name,,NO_SECT,type,offset
|
||||
pub const N_BINCL = 0x82; /// include file beginning: name,,NO_SECT,0,sum
|
||||
pub const N_SOL = 0x84; /// #included file name: name,,n_sect,0,address
|
||||
pub const N_PARAMS = 0x86; /// compiler parameters: name,,NO_SECT,0,0
|
||||
pub const N_VERSION = 0x88; /// compiler version: name,,NO_SECT,0,0
|
||||
pub const N_OLEVEL = 0x8A; /// compiler -O level: name,,NO_SECT,0,0
|
||||
pub const N_PSYM = 0xa0; /// parameter: name,,NO_SECT,type,offset
|
||||
pub const N_EINCL = 0xa2; /// include file end: name,,NO_SECT,0,0
|
||||
pub const N_ENTRY = 0xa4; /// alternate entry: name,,n_sect,linenumber,address
|
||||
pub const N_LBRAC = 0xc0; /// left bracket: 0,,NO_SECT,nesting level,address
|
||||
pub const N_EXCL = 0xc2; /// deleted include file: name,,NO_SECT,0,sum
|
||||
pub const N_RBRAC = 0xe0; /// right bracket: 0,,NO_SECT,nesting level,address
|
||||
pub const N_BCOMM = 0xe2; /// begin common: name,,NO_SECT,0,0
|
||||
pub const N_ECOMM = 0xe4; /// end common: name,,n_sect,0,0
|
||||
pub const N_ECOML = 0xe8; /// end common (local name): 0,,n_sect,0,address
|
||||
pub const N_LENG = 0xfe; /// second stab entry with length information
|
||||
/// section with only 4 byte literals
|
||||
pub const S_4BYTE_LITERALS = 0x3;
|
||||
|
||||
/// section with only 8 byte literals
|
||||
pub const S_8BYTE_LITERALS = 0x4;
|
||||
|
||||
/// section with only pointers to
|
||||
pub const S_LITERAL_POINTERS = 0x5;
|
||||
|
||||
/// if any of these bits set, a symbolic debugging entry
|
||||
pub const N_STAB = 0xe0;
|
||||
|
||||
/// private external symbol bit
|
||||
pub const N_PEXT = 0x10;
|
||||
|
||||
/// mask for the type bits
|
||||
pub const N_TYPE = 0x0e;
|
||||
|
||||
/// external symbol bit, set for external symbols
|
||||
pub const N_EXT = 0x01;
|
||||
|
||||
/// global symbol: name,,NO_SECT,type,0
|
||||
pub const N_GSYM = 0x20;
|
||||
|
||||
/// procedure name (f77 kludge): name,,NO_SECT,0,0
|
||||
pub const N_FNAME = 0x22;
|
||||
|
||||
/// procedure: name,,n_sect,linenumber,address
|
||||
pub const N_FUN = 0x24;
|
||||
|
||||
/// static symbol: name,,n_sect,type,address
|
||||
pub const N_STSYM = 0x26;
|
||||
|
||||
/// .lcomm symbol: name,,n_sect,type,address
|
||||
pub const N_LCSYM = 0x28;
|
||||
|
||||
/// begin nsect sym: 0,,n_sect,0,address
|
||||
pub const N_BNSYM = 0x2e;
|
||||
|
||||
/// AST file path: name,,NO_SECT,0,0
|
||||
pub const N_AST = 0x32;
|
||||
|
||||
/// emitted with gcc2_compiled and in gcc source
|
||||
pub const N_OPT = 0x3c;
|
||||
|
||||
/// register sym: name,,NO_SECT,type,register
|
||||
pub const N_RSYM = 0x40;
|
||||
|
||||
/// src line: 0,,n_sect,linenumber,address
|
||||
pub const N_SLINE = 0x44;
|
||||
|
||||
/// end nsect sym: 0,,n_sect,0,address
|
||||
pub const N_ENSYM = 0x4e;
|
||||
|
||||
/// structure elt: name,,NO_SECT,type,struct_offset
|
||||
pub const N_SSYM = 0x60;
|
||||
|
||||
/// source file name: name,,n_sect,0,address
|
||||
pub const N_SO = 0x64;
|
||||
|
||||
/// object file name: name,,0,0,st_mtime
|
||||
pub const N_OSO = 0x66;
|
||||
|
||||
/// local sym: name,,NO_SECT,type,offset
|
||||
pub const N_LSYM = 0x80;
|
||||
|
||||
/// include file beginning: name,,NO_SECT,0,sum
|
||||
pub const N_BINCL = 0x82;
|
||||
|
||||
/// #included file name: name,,n_sect,0,address
|
||||
pub const N_SOL = 0x84;
|
||||
|
||||
/// compiler parameters: name,,NO_SECT,0,0
|
||||
pub const N_PARAMS = 0x86;
|
||||
|
||||
/// compiler version: name,,NO_SECT,0,0
|
||||
pub const N_VERSION = 0x88;
|
||||
|
||||
/// compiler -O level: name,,NO_SECT,0,0
|
||||
pub const N_OLEVEL = 0x8A;
|
||||
|
||||
/// parameter: name,,NO_SECT,type,offset
|
||||
pub const N_PSYM = 0xa0;
|
||||
|
||||
/// include file end: name,,NO_SECT,0,0
|
||||
pub const N_EINCL = 0xa2;
|
||||
|
||||
/// alternate entry: name,,n_sect,linenumber,address
|
||||
pub const N_ENTRY = 0xa4;
|
||||
|
||||
/// left bracket: 0,,NO_SECT,nesting level,address
|
||||
pub const N_LBRAC = 0xc0;
|
||||
|
||||
/// deleted include file: name,,NO_SECT,0,sum
|
||||
pub const N_EXCL = 0xc2;
|
||||
|
||||
/// right bracket: 0,,NO_SECT,nesting level,address
|
||||
pub const N_RBRAC = 0xe0;
|
||||
|
||||
/// begin common: name,,NO_SECT,0,0
|
||||
pub const N_BCOMM = 0xe2;
|
||||
|
||||
/// end common: name,,n_sect,0,0
|
||||
pub const N_ECOMM = 0xe4;
|
||||
|
||||
/// end common (local name): 0,,n_sect,0,address
|
||||
pub const N_ECOML = 0xe8;
|
||||
|
||||
/// second stab entry with length information
|
||||
pub const N_LENG = 0xfe;
|
||||
|
||||
/// If a segment contains any sections marked with S_ATTR_DEBUG then all
|
||||
/// sections in that segment must have this attribute. No section other than
|
||||
@ -339,10 +685,10 @@ pub const N_LENG = 0xfe; /// second stab entry with length information
|
||||
/// a section type S_REGULAR. The static linker will not copy section contents
|
||||
/// from sections with this attribute into its output file. These sections
|
||||
/// generally contain DWARF debugging info.
|
||||
pub const S_ATTR_DEBUG = 0x02000000; /// a debug section
|
||||
/// a debug section
|
||||
pub const S_ATTR_DEBUG = 0x02000000;
|
||||
|
||||
pub const cpu_type_t = integer_t;
|
||||
pub const cpu_subtype_t = integer_t;
|
||||
pub const integer_t = c_int;
|
||||
pub const vm_prot_t = c_int;
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
const std = @import("../index.zig");
|
||||
const cstr = std.cstr;
|
||||
const unicode = std.unicode;
|
||||
const io = std.io;
|
||||
const os = std.os;
|
||||
const posix = os.posix;
|
||||
@ -12,6 +13,7 @@ const Buffer = std.Buffer;
|
||||
const builtin = @import("builtin");
|
||||
const Os = builtin.Os;
|
||||
const LinkedList = std.LinkedList;
|
||||
const windows_util = @import("windows/util.zig");
|
||||
|
||||
const is_windows = builtin.os == Os.windows;
|
||||
|
||||
@ -209,8 +211,8 @@ pub const ChildProcess = struct {
|
||||
defer Buffer.deinit(&stdout);
|
||||
defer Buffer.deinit(&stderr);
|
||||
|
||||
var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
|
||||
var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
|
||||
var stdout_file_in_stream = io.FileInStream.init(child.stdout.?);
|
||||
var stderr_file_in_stream = io.FileInStream.init(child.stderr.?);
|
||||
|
||||
try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size);
|
||||
try stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size);
|
||||
@ -520,8 +522,8 @@ pub const ChildProcess = struct {
|
||||
const cmd_line = try windowsCreateCommandLine(self.allocator, self.argv);
|
||||
defer self.allocator.free(cmd_line);
|
||||
|
||||
var siStartInfo = windows.STARTUPINFOA{
|
||||
.cb = @sizeOf(windows.STARTUPINFOA),
|
||||
var siStartInfo = windows.STARTUPINFOW{
|
||||
.cb = @sizeOf(windows.STARTUPINFOW),
|
||||
.hStdError = g_hChildStd_ERR_Wr,
|
||||
.hStdOutput = g_hChildStd_OUT_Wr,
|
||||
.hStdInput = g_hChildStd_IN_Rd,
|
||||
@ -545,7 +547,9 @@ pub const ChildProcess = struct {
|
||||
|
||||
const cwd_slice = if (self.cwd) |cwd| try cstr.addNullByte(self.allocator, cwd) else null;
|
||||
defer if (cwd_slice) |cwd| self.allocator.free(cwd);
|
||||
const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null;
|
||||
const cwd_w = if (cwd_slice) |cwd| try unicode.utf8ToUtf16LeWithNull(self.allocator, cwd) else null;
|
||||
defer if (cwd_w) |cwd| self.allocator.free(cwd);
|
||||
const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null;
|
||||
|
||||
const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null;
|
||||
defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf);
|
||||
@ -564,7 +568,13 @@ pub const ChildProcess = struct {
|
||||
};
|
||||
defer self.allocator.free(app_name);
|
||||
|
||||
windowsCreateProcess(app_name.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
|
||||
const app_name_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
|
||||
defer self.allocator.free(app_name_w);
|
||||
|
||||
const cmd_line_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, cmd_line);
|
||||
defer self.allocator.free(cmd_line_w);
|
||||
|
||||
windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
|
||||
if (no_path_err != error.FileNotFound) return no_path_err;
|
||||
|
||||
const PATH = try os.getEnvVarOwned(self.allocator, "PATH");
|
||||
@ -575,7 +585,10 @@ pub const ChildProcess = struct {
|
||||
const joined_path = try os.path.join(self.allocator, search_path, app_name);
|
||||
defer self.allocator.free(joined_path);
|
||||
|
||||
if (windowsCreateProcess(joined_path.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo)) |_| {
|
||||
const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
|
||||
defer self.allocator.free(joined_path_w);
|
||||
|
||||
if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
|
||||
break;
|
||||
} else |err| if (err == error.FileNotFound) {
|
||||
continue;
|
||||
@ -626,15 +639,36 @@ pub const ChildProcess = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn windowsCreateProcess(app_name: [*]u8, cmd_line: [*]u8, envp_ptr: ?[*]u8, cwd_ptr: ?[*]u8, lpStartupInfo: *windows.STARTUPINFOA, lpProcessInformation: *windows.PROCESS_INFORMATION) !void {
|
||||
if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) {
|
||||
fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, cwd_ptr: ?[*]u16, lpStartupInfo: *windows.STARTUPINFOW, lpProcessInformation: *windows.PROCESS_INFORMATION) !void {
|
||||
// TODO the docs for environment pointer say:
|
||||
// > A pointer to the environment block for the new process. If this parameter
|
||||
// > is NULL, the new process uses the environment of the calling process.
|
||||
// > ...
|
||||
// > An environment block can contain either Unicode or ANSI characters. If
|
||||
// > the environment block pointed to by lpEnvironment contains Unicode
|
||||
// > characters, be sure that dwCreationFlags includes CREATE_UNICODE_ENVIRONMENT.
|
||||
// > If this parameter is NULL and the environment block of the parent process
|
||||
// > contains Unicode characters, you must also ensure that dwCreationFlags
|
||||
// > includes CREATE_UNICODE_ENVIRONMENT.
|
||||
// This seems to imply that we have to somehow know whether our process parent passed
|
||||
// CREATE_UNICODE_ENVIRONMENT if we want to pass NULL for the environment parameter.
|
||||
// Since we do not know this information that would imply that we must not pass NULL
|
||||
// for the parameter.
|
||||
// However this would imply that programs compiled with -DUNICODE could not pass
|
||||
// environment variables to programs that were not, which seems unlikely.
|
||||
// More investigation is needed.
|
||||
if (windows.CreateProcessW(
|
||||
app_name, cmd_line, null, null, windows.TRUE, windows.CREATE_UNICODE_ENVIRONMENT,
|
||||
@ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation,
|
||||
) == 0) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
windows.ERROR.FILE_NOT_FOUND, windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
|
||||
switch (err) {
|
||||
windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
|
||||
windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
||||
windows.ERROR.INVALID_PARAMETER => unreachable,
|
||||
windows.ERROR.INVALID_NAME => error.InvalidName,
|
||||
else => os.unexpectedErrorWindows(err),
|
||||
};
|
||||
windows.ERROR.INVALID_NAME => return error.InvalidName,
|
||||
else => return os.unexpectedErrorWindows(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -48,18 +48,23 @@ pub const File = struct {
|
||||
return openReadC(&path_c);
|
||||
}
|
||||
if (is_windows) {
|
||||
const handle = try os.windowsOpen(
|
||||
path,
|
||||
windows.GENERIC_READ,
|
||||
windows.FILE_SHARE_READ,
|
||||
windows.OPEN_EXISTING,
|
||||
windows.FILE_ATTRIBUTE_NORMAL,
|
||||
);
|
||||
return openHandle(handle);
|
||||
const path_w = try windows_util.sliceToPrefixedFileW(path);
|
||||
return openReadW(&path_w);
|
||||
}
|
||||
@compileError("Unsupported OS");
|
||||
}
|
||||
|
||||
pub fn openReadW(path_w: [*]const u16) OpenError!File {
|
||||
const handle = try os.windowsOpenW(
|
||||
path_w,
|
||||
windows.GENERIC_READ,
|
||||
windows.FILE_SHARE_READ,
|
||||
windows.OPEN_EXISTING,
|
||||
windows.FILE_ATTRIBUTE_NORMAL,
|
||||
);
|
||||
return openHandle(handle);
|
||||
}
|
||||
|
||||
/// Calls `openWriteMode` with os.File.default_mode for the mode.
|
||||
pub fn openWrite(path: []const u8) OpenError!File {
|
||||
return openWriteMode(path, os.File.default_mode);
|
||||
@ -74,19 +79,24 @@ pub const File = struct {
|
||||
const fd = try os.posixOpen(path, flags, file_mode);
|
||||
return openHandle(fd);
|
||||
} else if (is_windows) {
|
||||
const handle = try os.windowsOpen(
|
||||
path,
|
||||
windows.GENERIC_WRITE,
|
||||
windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
|
||||
windows.CREATE_ALWAYS,
|
||||
windows.FILE_ATTRIBUTE_NORMAL,
|
||||
);
|
||||
return openHandle(handle);
|
||||
const path_w = try windows_util.sliceToPrefixedFileW(path);
|
||||
return openWriteModeW(&path_w, file_mode);
|
||||
} else {
|
||||
@compileError("TODO implement openWriteMode for this OS");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn openWriteModeW(path_w: [*]const u16, file_mode: Mode) OpenError!File {
|
||||
const handle = try os.windowsOpenW(
|
||||
path_w,
|
||||
windows.GENERIC_WRITE,
|
||||
windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
|
||||
windows.CREATE_ALWAYS,
|
||||
windows.FILE_ATTRIBUTE_NORMAL,
|
||||
);
|
||||
return openHandle(handle);
|
||||
}
|
||||
|
||||
/// If the path does not exist it will be created.
|
||||
/// If a file already exists in the destination this returns OpenError.PathAlreadyExists
|
||||
/// Call close to clean up.
|
||||
@ -96,19 +106,24 @@ pub const File = struct {
|
||||
const fd = try os.posixOpen(path, flags, file_mode);
|
||||
return openHandle(fd);
|
||||
} else if (is_windows) {
|
||||
const handle = try os.windowsOpen(
|
||||
path,
|
||||
windows.GENERIC_WRITE,
|
||||
windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
|
||||
windows.CREATE_NEW,
|
||||
windows.FILE_ATTRIBUTE_NORMAL,
|
||||
);
|
||||
return openHandle(handle);
|
||||
const path_w = try windows_util.sliceToPrefixedFileW(path);
|
||||
return openWriteNoClobberW(&path_w, file_mode);
|
||||
} else {
|
||||
@compileError("TODO implement openWriteMode for this OS");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn openWriteNoClobberW(path_w: [*]const u16, file_mode: Mode) OpenError!File {
|
||||
const handle = try os.windowsOpenW(
|
||||
path_w,
|
||||
windows.GENERIC_WRITE,
|
||||
windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
|
||||
windows.CREATE_NEW,
|
||||
windows.FILE_ATTRIBUTE_NORMAL,
|
||||
);
|
||||
return openHandle(handle);
|
||||
}
|
||||
|
||||
pub fn openHandle(handle: os.FileHandle) File {
|
||||
return File{ .handle = handle };
|
||||
}
|
||||
@ -190,17 +205,16 @@ pub const File = struct {
|
||||
|
||||
/// Upon success, the stream is in an uninitialized state. To continue using it,
|
||||
/// you must use the open() function.
|
||||
pub fn close(self: *File) void {
|
||||
pub fn close(self: File) void {
|
||||
os.close(self.handle);
|
||||
self.handle = undefined;
|
||||
}
|
||||
|
||||
/// Calls `os.isTty` on `self.handle`.
|
||||
pub fn isTty(self: *File) bool {
|
||||
pub fn isTty(self: File) bool {
|
||||
return os.isTty(self.handle);
|
||||
}
|
||||
|
||||
pub fn seekForward(self: *File, amount: isize) !void {
|
||||
pub fn seekForward(self: File, amount: isize) !void {
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.macosx, Os.ios => {
|
||||
const result = posix.lseek(self.handle, amount, posix.SEEK_CUR);
|
||||
@ -231,7 +245,7 @@ pub const File = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seekTo(self: *File, pos: usize) !void {
|
||||
pub fn seekTo(self: File, pos: usize) !void {
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.macosx, Os.ios => {
|
||||
const ipos = try math.cast(isize, pos);
|
||||
@ -256,6 +270,7 @@ pub const File = struct {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
windows.ERROR.INVALID_PARAMETER => unreachable,
|
||||
windows.ERROR.INVALID_HANDLE => unreachable,
|
||||
else => os.unexpectedErrorWindows(err),
|
||||
};
|
||||
}
|
||||
@ -264,7 +279,7 @@ pub const File = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getPos(self: *File) !usize {
|
||||
pub fn getPos(self: File) !usize {
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.macosx, Os.ios => {
|
||||
const result = posix.lseek(self.handle, 0, posix.SEEK_CUR);
|
||||
@ -300,7 +315,7 @@ pub const File = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getEndPos(self: *File) !usize {
|
||||
pub fn getEndPos(self: File) !usize {
|
||||
if (is_posix) {
|
||||
const stat = try os.posixFStat(self.handle);
|
||||
return @intCast(usize, stat.size);
|
||||
@ -325,7 +340,7 @@ pub const File = struct {
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn mode(self: *File) ModeError!Mode {
|
||||
pub fn mode(self: File) ModeError!Mode {
|
||||
if (is_posix) {
|
||||
var stat: posix.Stat = undefined;
|
||||
const err = posix.getErrno(posix.fstat(self.handle, &stat));
|
||||
@ -359,7 +374,7 @@ pub const File = struct {
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn read(self: *File, buffer: []u8) ReadError!usize {
|
||||
pub fn read(self: File, buffer: []u8) ReadError!usize {
|
||||
if (is_posix) {
|
||||
var index: usize = 0;
|
||||
while (index < buffer.len) {
|
||||
@ -407,7 +422,7 @@ pub const File = struct {
|
||||
|
||||
pub const WriteError = os.WindowsWriteError || os.PosixWriteError;
|
||||
|
||||
pub fn write(self: *File, bytes: []const u8) WriteError!void {
|
||||
pub fn write(self: File, bytes: []const u8) WriteError!void {
|
||||
if (is_posix) {
|
||||
try os.posixWrite(self.handle, bytes);
|
||||
} else if (is_windows) {
|
||||
|
||||
237
std/os/index.zig
237
std/os/index.zig
@ -57,6 +57,7 @@ pub const windowsWaitSingle = windows_util.windowsWaitSingle;
|
||||
pub const windowsWrite = windows_util.windowsWrite;
|
||||
pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
|
||||
pub const windowsOpen = windows_util.windowsOpen;
|
||||
pub const windowsOpenW = windows_util.windowsOpenW;
|
||||
pub const windowsLoadDll = windows_util.windowsLoadDll;
|
||||
pub const windowsUnloadDll = windows_util.windowsUnloadDll;
|
||||
pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
|
||||
@ -660,6 +661,7 @@ pub fn getBaseAddress() usize {
|
||||
return phdr - @sizeOf(ElfHeader);
|
||||
},
|
||||
builtin.Os.macosx => return @ptrToInt(&std.c._mh_execute_header),
|
||||
builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
}
|
||||
@ -817,37 +819,40 @@ test "os.getCwd" {
|
||||
|
||||
pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError;
|
||||
|
||||
pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void {
|
||||
/// TODO add a symLinkC variant
|
||||
pub fn symLink(existing_path: []const u8, new_path: []const u8) SymLinkError!void {
|
||||
if (is_windows) {
|
||||
return symLinkWindows(allocator, existing_path, new_path);
|
||||
return symLinkWindows(existing_path, new_path);
|
||||
} else {
|
||||
return symLinkPosix(allocator, existing_path, new_path);
|
||||
return symLinkPosix(existing_path, new_path);
|
||||
}
|
||||
}
|
||||
|
||||
pub const WindowsSymLinkError = error{
|
||||
OutOfMemory,
|
||||
NameTooLong,
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
|
||||
/// See https://github.com/ziglang/zig/issues/1396
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn symLinkWindows(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {
|
||||
const existing_with_null = try cstr.addNullByte(allocator, existing_path);
|
||||
defer allocator.free(existing_with_null);
|
||||
const new_with_null = try cstr.addNullByte(allocator, new_path);
|
||||
defer allocator.free(new_with_null);
|
||||
|
||||
if (windows.CreateSymbolicLinkA(existing_with_null.ptr, new_with_null.ptr, 0) == 0) {
|
||||
pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void {
|
||||
if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
else => unexpectedErrorWindows(err),
|
||||
};
|
||||
switch (err) {
|
||||
else => return unexpectedErrorWindows(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symLinkWindows(existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {
|
||||
const existing_path_w = try windows_util.sliceToPrefixedFileW(existing_path);
|
||||
const new_path_w = try windows_util.sliceToPrefixedFileW(new_path);
|
||||
return symLinkW(&existing_path_w, &new_path_w);
|
||||
}
|
||||
|
||||
pub const PosixSymLinkError = error{
|
||||
OutOfMemory,
|
||||
AccessDenied,
|
||||
DiskQuota,
|
||||
PathAlreadyExists,
|
||||
@ -864,43 +869,40 @@ pub const PosixSymLinkError = error{
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn symLinkPosix(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void {
|
||||
const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2);
|
||||
defer allocator.free(full_buf);
|
||||
|
||||
const existing_buf = full_buf;
|
||||
mem.copy(u8, existing_buf, existing_path);
|
||||
existing_buf[existing_path.len] = 0;
|
||||
|
||||
const new_buf = full_buf[existing_path.len + 1 ..];
|
||||
mem.copy(u8, new_buf, new_path);
|
||||
new_buf[new_path.len] = 0;
|
||||
|
||||
const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EFAULT, posix.EINVAL => unreachable,
|
||||
posix.EACCES, posix.EPERM => error.AccessDenied,
|
||||
posix.EDQUOT => error.DiskQuota,
|
||||
posix.EEXIST => error.PathAlreadyExists,
|
||||
posix.EIO => error.FileSystem,
|
||||
posix.ELOOP => error.SymLinkLoop,
|
||||
posix.ENAMETOOLONG => error.NameTooLong,
|
||||
posix.ENOENT => error.FileNotFound,
|
||||
posix.ENOTDIR => error.NotDir,
|
||||
posix.ENOMEM => error.SystemResources,
|
||||
posix.ENOSPC => error.NoSpaceLeft,
|
||||
posix.EROFS => error.ReadOnlyFileSystem,
|
||||
else => unexpectedErrorPosix(err),
|
||||
};
|
||||
pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void {
|
||||
const err = posix.getErrno(posix.symlink(existing_path, new_path));
|
||||
switch (err) {
|
||||
0 => return,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.EACCES => return error.AccessDenied,
|
||||
posix.EPERM => return error.AccessDenied,
|
||||
posix.EDQUOT => return error.DiskQuota,
|
||||
posix.EEXIST => return error.PathAlreadyExists,
|
||||
posix.EIO => return error.FileSystem,
|
||||
posix.ELOOP => return error.SymLinkLoop,
|
||||
posix.ENAMETOOLONG => return error.NameTooLong,
|
||||
posix.ENOENT => return error.FileNotFound,
|
||||
posix.ENOTDIR => return error.NotDir,
|
||||
posix.ENOMEM => return error.SystemResources,
|
||||
posix.ENOSPC => return error.NoSpaceLeft,
|
||||
posix.EROFS => return error.ReadOnlyFileSystem,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symLinkPosix(existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void {
|
||||
const existing_path_c = try toPosixPath(existing_path);
|
||||
const new_path_c = try toPosixPath(new_path);
|
||||
return symLinkPosixC(&existing_path_c, &new_path_c);
|
||||
}
|
||||
|
||||
// here we replace the standard +/ with -_ so that it can be used in a file name
|
||||
const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);
|
||||
|
||||
/// TODO remove the allocator requirement from this API
|
||||
pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void {
|
||||
if (symLink(allocator, existing_path, new_path)) {
|
||||
if (symLink(existing_path, new_path)) {
|
||||
return;
|
||||
} else |err| switch (err) {
|
||||
error.PathAlreadyExists => {},
|
||||
@ -918,7 +920,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
|
||||
try getRandomBytes(rand_buf[0..]);
|
||||
b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
|
||||
|
||||
if (symLink(allocator, existing_path, tmp_path)) {
|
||||
if (symLink(existing_path, tmp_path)) {
|
||||
return rename(tmp_path, new_path);
|
||||
} else |err| switch (err) {
|
||||
error.PathAlreadyExists => continue,
|
||||
@ -1250,54 +1252,70 @@ pub const DeleteDirError = error{
|
||||
NotDir,
|
||||
DirNotEmpty,
|
||||
ReadOnlyFileSystem,
|
||||
OutOfMemory,
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
|
||||
/// See https://github.com/ziglang/zig/issues/1396
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
/// Returns ::error.DirNotEmpty if the directory is not empty.
|
||||
/// To delete a directory recursively, see ::deleteTree
|
||||
pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) DeleteDirError!void {
|
||||
const path_buf = try allocator.alloc(u8, dir_path.len + 1);
|
||||
defer allocator.free(path_buf);
|
||||
|
||||
mem.copy(u8, path_buf, dir_path);
|
||||
path_buf[dir_path.len] = 0;
|
||||
|
||||
pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void {
|
||||
switch (builtin.os) {
|
||||
Os.windows => {
|
||||
if (windows.RemoveDirectoryA(path_buf.ptr) == 0) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
|
||||
windows.ERROR.DIR_NOT_EMPTY => error.DirNotEmpty,
|
||||
else => unexpectedErrorWindows(err),
|
||||
};
|
||||
}
|
||||
const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path);
|
||||
return deleteDirW(&dir_path_w);
|
||||
},
|
||||
Os.linux, Os.macosx, Os.ios => {
|
||||
const err = posix.getErrno(posix.rmdir(path_buf.ptr));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EACCES, posix.EPERM => error.AccessDenied,
|
||||
posix.EBUSY => error.FileBusy,
|
||||
posix.EFAULT, posix.EINVAL => unreachable,
|
||||
posix.ELOOP => error.SymLinkLoop,
|
||||
posix.ENAMETOOLONG => error.NameTooLong,
|
||||
posix.ENOENT => error.FileNotFound,
|
||||
posix.ENOMEM => error.SystemResources,
|
||||
posix.ENOTDIR => error.NotDir,
|
||||
posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,
|
||||
posix.EROFS => error.ReadOnlyFileSystem,
|
||||
else => unexpectedErrorPosix(err),
|
||||
};
|
||||
const err = posix.getErrno(posix.rmdir(dir_path));
|
||||
switch (err) {
|
||||
0 => return,
|
||||
posix.EACCES => return error.AccessDenied,
|
||||
posix.EPERM => return error.AccessDenied,
|
||||
posix.EBUSY => return error.FileBusy,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.ELOOP => return error.SymLinkLoop,
|
||||
posix.ENAMETOOLONG => return error.NameTooLong,
|
||||
posix.ENOENT => return error.FileNotFound,
|
||||
posix.ENOMEM => return error.SystemResources,
|
||||
posix.ENOTDIR => return error.NotDir,
|
||||
posix.EEXIST => return error.DirNotEmpty,
|
||||
posix.ENOTEMPTY => return error.DirNotEmpty,
|
||||
posix.EROFS => return error.ReadOnlyFileSystem,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
},
|
||||
else => @compileError("unimplemented"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void {
|
||||
if (windows.RemoveDirectoryW(dir_path_w) == 0) {
|
||||
const err = windows.GetLastError();
|
||||
switch (err) {
|
||||
windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
||||
windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty,
|
||||
else => return unexpectedErrorWindows(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns ::error.DirNotEmpty if the directory is not empty.
|
||||
/// To delete a directory recursively, see ::deleteTree
|
||||
pub fn deleteDir(dir_path: []const u8) DeleteDirError!void {
|
||||
switch (builtin.os) {
|
||||
Os.windows => {
|
||||
const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path);
|
||||
return deleteDirW(&dir_path_w);
|
||||
},
|
||||
Os.linux, Os.macosx, Os.ios => {
|
||||
const dir_path_c = try toPosixPath(dir_path);
|
||||
return deleteDirC(&dir_path_c);
|
||||
},
|
||||
else => @compileError("unimplemented"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether ::full_path describes a symlink, file, or directory, this function
|
||||
/// removes it. If it cannot be removed because it is a non-empty directory,
|
||||
/// this function recursively removes its entries and then tries again.
|
||||
@ -1344,6 +1362,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
|
||||
error.IsDir => {},
|
||||
error.AccessDenied => got_access_denied = true,
|
||||
|
||||
error.InvalidUtf8,
|
||||
error.SymLinkLoop,
|
||||
error.NameTooLong,
|
||||
error.SystemResources,
|
||||
@ -1351,7 +1370,6 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
|
||||
error.NotDir,
|
||||
error.FileSystem,
|
||||
error.FileBusy,
|
||||
error.InvalidUtf8,
|
||||
error.BadPathName,
|
||||
error.Unexpected,
|
||||
=> return err,
|
||||
@ -1379,6 +1397,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
|
||||
error.NoSpaceLeft,
|
||||
error.PathAlreadyExists,
|
||||
error.Unexpected,
|
||||
error.InvalidUtf8,
|
||||
error.BadPathName,
|
||||
=> return err,
|
||||
};
|
||||
defer dir.close();
|
||||
@ -1396,7 +1416,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
|
||||
try deleteTree(allocator, full_entry_path);
|
||||
}
|
||||
}
|
||||
return deleteDir(allocator, full_path);
|
||||
return deleteDir(full_path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1420,8 +1440,9 @@ pub const Dir = struct {
|
||||
},
|
||||
Os.windows => struct {
|
||||
handle: windows.HANDLE,
|
||||
find_file_data: windows.WIN32_FIND_DATAA,
|
||||
find_file_data: windows.WIN32_FIND_DATAW,
|
||||
first: bool,
|
||||
name_data: [256]u8,
|
||||
},
|
||||
else => @compileError("unimplemented"),
|
||||
};
|
||||
@ -1458,6 +1479,8 @@ pub const Dir = struct {
|
||||
NoSpaceLeft,
|
||||
PathAlreadyExists,
|
||||
OutOfMemory,
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
|
||||
/// See https://github.com/ziglang/zig/issues/1396
|
||||
Unexpected,
|
||||
@ -1469,12 +1492,13 @@ pub const Dir = struct {
|
||||
.allocator = allocator,
|
||||
.handle = switch (builtin.os) {
|
||||
Os.windows => blk: {
|
||||
var find_file_data: windows.WIN32_FIND_DATAA = undefined;
|
||||
const handle = try windows_util.windowsFindFirstFile(allocator, dir_path, &find_file_data);
|
||||
var find_file_data: windows.WIN32_FIND_DATAW = undefined;
|
||||
const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data);
|
||||
break :blk Handle{
|
||||
.handle = handle,
|
||||
.find_file_data = find_file_data, // TODO guaranteed copy elision
|
||||
.first = true,
|
||||
.name_data = undefined,
|
||||
};
|
||||
},
|
||||
Os.macosx, Os.ios => Handle{
|
||||
@ -1589,9 +1613,12 @@ pub const Dir = struct {
|
||||
if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data))
|
||||
return null;
|
||||
}
|
||||
const name = std.cstr.toSlice(self.handle.find_file_data.cFileName[0..].ptr);
|
||||
if (mem.eql(u8, name, ".") or mem.eql(u8, name, ".."))
|
||||
const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);
|
||||
if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{'.', '.'}))
|
||||
continue;
|
||||
// Trust that Windows gives us valid UTF-16LE
|
||||
const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable;
|
||||
const name_utf8 = self.handle.name_data[0..name_utf8_len];
|
||||
const kind = blk: {
|
||||
const attrs = self.handle.find_file_data.dwFileAttributes;
|
||||
if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
|
||||
@ -1600,7 +1627,7 @@ pub const Dir = struct {
|
||||
break :blk Entry.Kind.Unknown;
|
||||
};
|
||||
return Entry{
|
||||
.name = name,
|
||||
.name = name_utf8,
|
||||
.kind = kind,
|
||||
};
|
||||
}
|
||||
@ -2087,8 +2114,9 @@ pub fn unexpectedErrorPosix(errno: usize) UnexpectedError {
|
||||
/// Call this when you made a windows DLL call or something that does SetLastError
|
||||
/// and you get an unexpected error.
|
||||
pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
|
||||
if (true) {
|
||||
if (unexpected_error_tracing) {
|
||||
debug.warn("unexpected GetLastError(): {}\n", err);
|
||||
@breakpoint();
|
||||
debug.dumpCurrentStackTrace(null);
|
||||
}
|
||||
return error.Unexpected;
|
||||
@ -2103,17 +2131,35 @@ pub fn openSelfExe() !os.File {
|
||||
buf[self_exe_path.len] = 0;
|
||||
return os.File.openReadC(self_exe_path.ptr);
|
||||
},
|
||||
Os.windows => {
|
||||
var buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
|
||||
const wide_slice = try selfExePathW(&buf);
|
||||
return os.File.openReadW(wide_slice.ptr);
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
}
|
||||
|
||||
test "openSelfExe" {
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),
|
||||
else => return error.SkipZigTest, // Unsupported OS
|
||||
Os.linux, Os.macosx, Os.ios, Os.windows => (try openSelfExe()).close(),
|
||||
else => return error.SkipZigTest, // Unsupported OS.
|
||||
}
|
||||
}
|
||||
|
||||
pub fn selfExePathW(out_buffer: *[windows_util.PATH_MAX_WIDE]u16) ![]u16 {
|
||||
const casted_len = @intCast(windows.DWORD, out_buffer.len); // TODO shouldn't need this cast
|
||||
const rc = windows.GetModuleFileNameW(null, out_buffer, casted_len);
|
||||
assert(rc <= out_buffer.len);
|
||||
if (rc == 0) {
|
||||
const err = windows.GetLastError();
|
||||
switch (err) {
|
||||
else => return unexpectedErrorWindows(err),
|
||||
}
|
||||
}
|
||||
return out_buffer[0..rc];
|
||||
}
|
||||
|
||||
/// Get the path to the current executable.
|
||||
/// If you only need the directory, use selfExeDirPath.
|
||||
/// If you only want an open file handle, use openSelfExe.
|
||||
@ -2129,16 +2175,7 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
|
||||
Os.linux => return readLink(out_buffer, "/proc/self/exe"),
|
||||
Os.windows => {
|
||||
var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
|
||||
const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast
|
||||
const rc = windows.GetModuleFileNameW(null, &utf16le_buf, casted_len);
|
||||
assert(rc <= utf16le_buf.len);
|
||||
if (rc == 0) {
|
||||
const err = windows.GetLastError();
|
||||
switch (err) {
|
||||
else => return unexpectedErrorWindows(err),
|
||||
}
|
||||
}
|
||||
const utf16le_slice = utf16le_buf[0..rc];
|
||||
const utf16le_slice = try selfExePathW(&utf16le_buf);
|
||||
// Trust that Windows gives us valid UTF-16LE.
|
||||
const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable;
|
||||
return out_buffer[0..end_index];
|
||||
|
||||
@ -23,11 +23,22 @@ pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlag
|
||||
|
||||
pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL;
|
||||
|
||||
pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, ulOptions: DWORD, samDesired: REGSAM,
|
||||
phkResult: &HKEY,) LSTATUS;
|
||||
pub extern "advapi32" stdcallcc fn RegOpenKeyExW(
|
||||
hKey: HKEY,
|
||||
lpSubKey: LPCWSTR,
|
||||
ulOptions: DWORD,
|
||||
samDesired: REGSAM,
|
||||
phkResult: &HKEY,
|
||||
) LSTATUS;
|
||||
|
||||
pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD,
|
||||
lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS;
|
||||
pub extern "advapi32" stdcallcc fn RegQueryValueExW(
|
||||
hKey: HKEY,
|
||||
lpValueName: LPCWSTR,
|
||||
lpReserved: LPDWORD,
|
||||
lpType: LPDWORD,
|
||||
lpData: LPBYTE,
|
||||
lpcbData: LPDWORD,
|
||||
) LSTATUS;
|
||||
|
||||
// RtlGenRandom is known as SystemFunction036 under advapi32
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */
|
||||
|
||||
@ -3,10 +3,9 @@ const assert = std.debug.assert;
|
||||
|
||||
pub use @import("advapi32.zig");
|
||||
pub use @import("kernel32.zig");
|
||||
pub use @import("ntdll.zig");
|
||||
pub use @import("ole32.zig");
|
||||
pub use @import("shell32.zig");
|
||||
pub use @import("shlwapi.zig");
|
||||
pub use @import("user32.zig");
|
||||
|
||||
test "import" {
|
||||
_ = @import("util.zig");
|
||||
@ -14,6 +13,7 @@ test "import" {
|
||||
|
||||
pub const ERROR = @import("error.zig");
|
||||
|
||||
pub const SHORT = c_short;
|
||||
pub const BOOL = c_int;
|
||||
pub const BOOLEAN = BYTE;
|
||||
pub const BYTE = u8;
|
||||
@ -172,11 +172,11 @@ pub const PROCESS_INFORMATION = extern struct {
|
||||
dwThreadId: DWORD,
|
||||
};
|
||||
|
||||
pub const STARTUPINFOA = extern struct {
|
||||
pub const STARTUPINFOW = extern struct {
|
||||
cb: DWORD,
|
||||
lpReserved: ?LPSTR,
|
||||
lpDesktop: ?LPSTR,
|
||||
lpTitle: ?LPSTR,
|
||||
lpReserved: ?LPWSTR,
|
||||
lpDesktop: ?LPWSTR,
|
||||
lpTitle: ?LPWSTR,
|
||||
dwX: DWORD,
|
||||
dwY: DWORD,
|
||||
dwXSize: DWORD,
|
||||
@ -236,7 +236,7 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;
|
||||
pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;
|
||||
pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
|
||||
|
||||
pub const WIN32_FIND_DATAA = extern struct {
|
||||
pub const WIN32_FIND_DATAW = extern struct {
|
||||
dwFileAttributes: DWORD,
|
||||
ftCreationTime: FILETIME,
|
||||
ftLastAccessTime: FILETIME,
|
||||
@ -245,8 +245,8 @@ pub const WIN32_FIND_DATAA = extern struct {
|
||||
nFileSizeLow: DWORD,
|
||||
dwReserved0: DWORD,
|
||||
dwReserved1: DWORD,
|
||||
cFileName: [260]CHAR,
|
||||
cAlternateFileName: [14]CHAR,
|
||||
cFileName: [260]u16,
|
||||
cAlternateFileName: [14]u16,
|
||||
};
|
||||
|
||||
pub const FILETIME = extern struct {
|
||||
@ -288,27 +288,27 @@ pub const GUID = extern struct {
|
||||
assert(str[index] == '{');
|
||||
index += 1;
|
||||
|
||||
guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index..index + 8], 16) catch unreachable;
|
||||
guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index .. index + 8], 16) catch unreachable;
|
||||
index += 8;
|
||||
|
||||
assert(str[index] == '-');
|
||||
index += 1;
|
||||
|
||||
guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable;
|
||||
guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable;
|
||||
index += 4;
|
||||
|
||||
assert(str[index] == '-');
|
||||
index += 1;
|
||||
|
||||
guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable;
|
||||
guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable;
|
||||
index += 4;
|
||||
|
||||
assert(str[index] == '-');
|
||||
index += 1;
|
||||
|
||||
guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
|
||||
guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable;
|
||||
index += 2;
|
||||
guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
|
||||
guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable;
|
||||
index += 2;
|
||||
|
||||
assert(str[index] == '-');
|
||||
@ -316,7 +316,7 @@ pub const GUID = extern struct {
|
||||
|
||||
var i: usize = 2;
|
||||
while (i < guid.Data4.len) : (i += 1) {
|
||||
guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
|
||||
guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable;
|
||||
index += 2;
|
||||
}
|
||||
|
||||
@ -363,3 +363,17 @@ pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000;
|
||||
pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
|
||||
pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
|
||||
pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
|
||||
|
||||
pub const SMALL_RECT = extern struct {
|
||||
Left: SHORT,
|
||||
Top: SHORT,
|
||||
Right: SHORT,
|
||||
Bottom: SHORT,
|
||||
};
|
||||
|
||||
pub const COORD = extern struct {
|
||||
X: SHORT,
|
||||
Y: SHORT,
|
||||
};
|
||||
|
||||
pub const CREATE_UNICODE_ENVIRONMENT = 1024;
|
||||
|
||||
@ -4,19 +4,8 @@ pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVE
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateDirectoryA(lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateFileA(
|
||||
lpFileName: [*]const u8, // TODO null terminated pointer type
|
||||
dwDesiredAccess: DWORD,
|
||||
dwShareMode: DWORD,
|
||||
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
|
||||
dwCreationDisposition: DWORD,
|
||||
dwFlagsAndAttributes: DWORD,
|
||||
hTemplateFile: ?HANDLE,
|
||||
) HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateFileW(
|
||||
lpFileName: [*]const u16, // TODO null terminated pointer type
|
||||
dwDesiredAccess: DWORD,
|
||||
@ -34,37 +23,32 @@ pub extern "kernel32" stdcallcc fn CreatePipe(
|
||||
nSize: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateProcessA(
|
||||
lpApplicationName: ?LPCSTR,
|
||||
lpCommandLine: LPSTR,
|
||||
pub extern "kernel32" stdcallcc fn CreateProcessW(
|
||||
lpApplicationName: ?LPWSTR,
|
||||
lpCommandLine: LPWSTR,
|
||||
lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
|
||||
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
|
||||
bInheritHandles: BOOL,
|
||||
dwCreationFlags: DWORD,
|
||||
lpEnvironment: ?*c_void,
|
||||
lpCurrentDirectory: ?LPCSTR,
|
||||
lpStartupInfo: *STARTUPINFOA,
|
||||
lpCurrentDirectory: ?LPWSTR,
|
||||
lpStartupInfo: *STARTUPINFOW,
|
||||
lpProcessInformation: *PROCESS_INFORMATION,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
|
||||
lpSymlinkFileName: LPCSTR,
|
||||
lpTargetFileName: LPCSTR,
|
||||
dwFlags: DWORD,
|
||||
) BOOLEAN;
|
||||
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkW(lpSymlinkFileName: [*]const u16, lpTargetFileName: [*]const u16, dwFlags: DWORD) BOOLEAN;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: [*]const u8) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
|
||||
pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE;
|
||||
pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn FindNextFileW(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAW) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
|
||||
|
||||
@ -72,7 +56,8 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: DWORD, lpBuffer: ?[*]CHAR) DWORD;
|
||||
pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE;
|
||||
@ -86,12 +71,12 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD;
|
||||
pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD;
|
||||
pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
|
||||
@ -101,13 +86,6 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
|
||||
in_dwBufferSize: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
|
||||
hFile: HANDLE,
|
||||
lpszFilePath: LPSTR,
|
||||
cchFilePath: DWORD,
|
||||
dwFlags: DWORD,
|
||||
) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW(
|
||||
hFile: HANDLE,
|
||||
lpszFilePath: [*]u16,
|
||||
@ -138,12 +116,6 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
|
||||
|
||||
pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn MoveFileExA(
|
||||
lpExistingFileName: [*]const u8,
|
||||
lpNewFileName: [*]const u8,
|
||||
dwFlags: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn MoveFileExW(
|
||||
lpExistingFileName: [*]const u16,
|
||||
lpNewFileName: [*]const u16,
|
||||
@ -175,7 +147,9 @@ pub extern "kernel32" stdcallcc fn ReadFile(
|
||||
in_out_lpOverlapped: ?*OVERLAPPED,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn RemoveDirectoryW(lpPathName: [*]const u16) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn SetFilePointerEx(
|
||||
in_fFile: HANDLE,
|
||||
@ -202,8 +176,7 @@ pub extern "kernel32" stdcallcc fn WriteFile(
|
||||
|
||||
pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL;
|
||||
|
||||
//TODO: call unicode versions instead of relying on ANSI code page
|
||||
pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
|
||||
pub extern "kernel32" stdcallcc fn LoadLibraryW(lpLibFileName: [*]const u16) ?HMODULE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
|
||||
|
||||
@ -232,3 +205,17 @@ pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16;
|
||||
pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2;
|
||||
pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1;
|
||||
pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4;
|
||||
|
||||
|
||||
pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct {
|
||||
dwSize: COORD,
|
||||
dwCursorPosition: COORD,
|
||||
wAttributes: WORD,
|
||||
srWindow: SMALL_RECT,
|
||||
dwMaximumWindowSize: COORD,
|
||||
};
|
||||
|
||||
pub const FOREGROUND_BLUE = 1;
|
||||
pub const FOREGROUND_GREEN = 2;
|
||||
pub const FOREGROUND_RED = 4;
|
||||
pub const FOREGROUND_INTENSITY = 8;
|
||||
|
||||
3
std/os/windows/ntdll.zig
Normal file
3
std/os/windows/ntdll.zig
Normal file
@ -0,0 +1,3 @@
|
||||
use @import("index.zig");
|
||||
|
||||
pub extern "NtDll" stdcallcc fn RtlCaptureStackBackTrace(FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **c_void, BackTraceHash: ?*DWORD) WORD;
|
||||
@ -5,7 +5,6 @@ pub extern "ole32.dll" stdcallcc fn CoUninitialize() void;
|
||||
pub extern "ole32.dll" stdcallcc fn CoGetCurrentProcess() DWORD;
|
||||
pub extern "ole32.dll" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT;
|
||||
|
||||
|
||||
pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED;
|
||||
pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED;
|
||||
pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE;
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
use @import("index.zig");
|
||||
|
||||
pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
use @import("index.zig");
|
||||
|
||||
pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
const std = @import("../../index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const os = std.os;
|
||||
const unicode = std.unicode;
|
||||
const windows = std.os.windows;
|
||||
const assert = std.debug.assert;
|
||||
const mem = std.mem;
|
||||
@ -118,16 +119,14 @@ pub const OpenError = error{
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn windowsOpen(
|
||||
file_path: []const u8,
|
||||
pub fn windowsOpenW(
|
||||
file_path_w: [*]const u16,
|
||||
desired_access: windows.DWORD,
|
||||
share_mode: windows.DWORD,
|
||||
creation_disposition: windows.DWORD,
|
||||
flags_and_attrs: windows.DWORD,
|
||||
) OpenError!windows.HANDLE {
|
||||
const file_path_w = try sliceToPrefixedFileW(file_path);
|
||||
|
||||
const result = windows.CreateFileW(&file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null);
|
||||
const result = windows.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null);
|
||||
|
||||
if (result == windows.INVALID_HANDLE_VALUE) {
|
||||
const err = windows.GetLastError();
|
||||
@ -146,42 +145,63 @@ pub fn windowsOpen(
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn windowsOpen(
|
||||
file_path: []const u8,
|
||||
desired_access: windows.DWORD,
|
||||
share_mode: windows.DWORD,
|
||||
creation_disposition: windows.DWORD,
|
||||
flags_and_attrs: windows.DWORD,
|
||||
) OpenError!windows.HANDLE {
|
||||
const file_path_w = try sliceToPrefixedFileW(file_path);
|
||||
return windowsOpenW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs);
|
||||
}
|
||||
|
||||
/// Caller must free result.
|
||||
pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u8 {
|
||||
pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 {
|
||||
// count bytes needed
|
||||
const bytes_needed = x: {
|
||||
var bytes_needed: usize = 1; // 1 for the final null byte
|
||||
const max_chars_needed = x: {
|
||||
var max_chars_needed: usize = 1; // 1 for the final null byte
|
||||
var it = env_map.iterator();
|
||||
while (it.next()) |pair| {
|
||||
// +1 for '='
|
||||
// +1 for null byte
|
||||
bytes_needed += pair.key.len + pair.value.len + 2;
|
||||
max_chars_needed += pair.key.len + pair.value.len + 2;
|
||||
}
|
||||
break :x bytes_needed;
|
||||
break :x max_chars_needed;
|
||||
};
|
||||
const result = try allocator.alloc(u8, bytes_needed);
|
||||
const result = try allocator.alloc(u16, max_chars_needed);
|
||||
errdefer allocator.free(result);
|
||||
|
||||
var it = env_map.iterator();
|
||||
var i: usize = 0;
|
||||
while (it.next()) |pair| {
|
||||
mem.copy(u8, result[i..], pair.key);
|
||||
i += pair.key.len;
|
||||
i += try unicode.utf8ToUtf16Le(result[i..], pair.key);
|
||||
result[i] = '=';
|
||||
i += 1;
|
||||
mem.copy(u8, result[i..], pair.value);
|
||||
i += pair.value.len;
|
||||
i += try unicode.utf8ToUtf16Le(result[i..], pair.value);
|
||||
result[i] = 0;
|
||||
i += 1;
|
||||
}
|
||||
result[i] = 0;
|
||||
return result;
|
||||
i += 1;
|
||||
return allocator.shrink(u16, result, i);
|
||||
}
|
||||
|
||||
pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE {
|
||||
const padded_buff = try cstr.addNullByte(allocator, dll_path);
|
||||
defer allocator.free(padded_buff);
|
||||
return windows.LoadLibraryA(padded_buff.ptr) orelse error.DllNotFound;
|
||||
pub fn windowsLoadDllW(dll_path_w: [*]const u16) !windows.HMODULE {
|
||||
return windows.LoadLibraryW(dll_path_w) orelse {
|
||||
const err = windows.GetLastError();
|
||||
switch (err) {
|
||||
windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
|
||||
windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
||||
windows.ERROR.MOD_NOT_FOUND => return error.FileNotFound,
|
||||
else => return os.unexpectedErrorWindows(err),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn windowsLoadDll(dll_path: []const u8) !windows.HMODULE {
|
||||
const dll_path_w = try sliceToPrefixedFileW(dll_path);
|
||||
return windowsLoadDllW(&dll_path_w);
|
||||
}
|
||||
|
||||
pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
|
||||
@ -191,27 +211,19 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
|
||||
test "InvalidDll" {
|
||||
if (builtin.os != builtin.Os.windows) return error.SkipZigTest;
|
||||
|
||||
const DllName = "asdf.dll";
|
||||
const allocator = std.debug.global_allocator;
|
||||
const handle = os.windowsLoadDll(allocator, DllName) catch |err| {
|
||||
assert(err == error.DllNotFound);
|
||||
const handle = os.windowsLoadDll("asdf.dll") catch |err| {
|
||||
assert(err == error.FileNotFound);
|
||||
return;
|
||||
};
|
||||
@panic("Expected error from function");
|
||||
}
|
||||
|
||||
pub fn windowsFindFirstFile(
|
||||
allocator: *mem.Allocator,
|
||||
dir_path: []const u8,
|
||||
find_file_data: *windows.WIN32_FIND_DATAA,
|
||||
find_file_data: *windows.WIN32_FIND_DATAW,
|
||||
) !windows.HANDLE {
|
||||
const wild_and_null = []u8{ '\\', '*', 0 };
|
||||
const path_with_wild_and_null = try allocator.alloc(u8, dir_path.len + wild_and_null.len);
|
||||
defer allocator.free(path_with_wild_and_null);
|
||||
|
||||
mem.copy(u8, path_with_wild_and_null, dir_path);
|
||||
mem.copy(u8, path_with_wild_and_null[dir_path.len..], wild_and_null);
|
||||
|
||||
const handle = windows.FindFirstFileA(path_with_wild_and_null.ptr, find_file_data);
|
||||
const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{'\\', '*', 0});
|
||||
const handle = windows.FindFirstFileW(&dir_path_w, find_file_data);
|
||||
|
||||
if (handle == windows.INVALID_HANDLE_VALUE) {
|
||||
const err = windows.GetLastError();
|
||||
@ -226,8 +238,8 @@ pub fn windowsFindFirstFile(
|
||||
}
|
||||
|
||||
/// Returns `true` if there was another file, `false` otherwise.
|
||||
pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAA) !bool {
|
||||
if (windows.FindNextFileA(handle, find_file_data) == 0) {
|
||||
pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool {
|
||||
if (windows.FindNextFileW(handle, find_file_data) == 0) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
windows.ERROR.NO_MORE_FILES => false,
|
||||
@ -288,8 +300,12 @@ pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
|
||||
}
|
||||
|
||||
pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
|
||||
return sliceToPrefixedSuffixedFileW(s, []u16{0});
|
||||
}
|
||||
|
||||
pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 {
|
||||
// TODO well defined copy elision
|
||||
var result: [PATH_MAX_WIDE + 1]u16 = undefined;
|
||||
var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined;
|
||||
|
||||
// > File I/O functions in the Windows API convert "/" to "\" as part of
|
||||
// > converting the name to an NT-style name, except when using the "\\?\"
|
||||
@ -297,11 +313,12 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
|
||||
// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
|
||||
// Because we want the larger maximum path length for absolute paths, we
|
||||
// disallow forward slashes in zig std lib file functions on Windows.
|
||||
for (s) |byte|
|
||||
for (s) |byte| {
|
||||
switch (byte) {
|
||||
'/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
|
||||
else => {},
|
||||
};
|
||||
'/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: {
|
||||
const prefix = []u16{ '\\', '\\', '?', '\\' };
|
||||
mem.copy(u16, result[0..], prefix);
|
||||
@ -309,7 +326,7 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
|
||||
};
|
||||
const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s);
|
||||
assert(end_index <= result.len);
|
||||
if (end_index == result.len) return error.NameTooLong;
|
||||
result[end_index] = 0;
|
||||
if (end_index + suffix.len > result.len) return error.NameTooLong;
|
||||
mem.copy(u16, result[end_index..], suffix);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -6,32 +6,32 @@ const assert = std.debug.assert;
|
||||
//////////////////////////
|
||||
|
||||
pub const Message = struct {
|
||||
sender: MailboxId,
|
||||
sender: MailboxId,
|
||||
receiver: MailboxId,
|
||||
code: usize,
|
||||
args: [5]usize,
|
||||
payload: ?[]const u8,
|
||||
code: usize,
|
||||
args: [5]usize,
|
||||
payload: ?[]const u8,
|
||||
|
||||
pub fn from(mailbox_id: *const MailboxId) Message {
|
||||
return Message {
|
||||
.sender = MailboxId.Undefined,
|
||||
return Message{
|
||||
.sender = MailboxId.Undefined,
|
||||
.receiver = mailbox_id.*,
|
||||
.code = undefined,
|
||||
.args = undefined,
|
||||
.payload = null,
|
||||
.code = undefined,
|
||||
.args = undefined,
|
||||
.payload = null,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn to(mailbox_id: *const MailboxId, msg_code: usize, args: ...) Message {
|
||||
var message = Message {
|
||||
.sender = MailboxId.This,
|
||||
var message = Message{
|
||||
.sender = MailboxId.This,
|
||||
.receiver = mailbox_id.*,
|
||||
.code = msg_code,
|
||||
.args = undefined,
|
||||
.payload = null,
|
||||
.code = msg_code,
|
||||
.args = undefined,
|
||||
.payload = null,
|
||||
};
|
||||
|
||||
assert (args.len <= message.args.len);
|
||||
assert(args.len <= message.args.len);
|
||||
comptime var i = 0;
|
||||
inline while (i < args.len) : (i += 1) {
|
||||
message.args[i] = args[i];
|
||||
@ -111,8 +111,7 @@ pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
|
||||
pub fn write(fd: i32, buf: [*]const u8, count: usize) usize {
|
||||
switch (fd) {
|
||||
STDOUT_FILENO, STDERR_FILENO => {
|
||||
send(Message.to(Server.Terminal, 1)
|
||||
.withPayload(buf[0..count]));
|
||||
send(Message.to(Server.Terminal, 1).withPayload(buf[0..count]));
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -124,14 +123,14 @@ pub fn write(fd: i32, buf: [*]const u8, count: usize) usize {
|
||||
///////////////////////////
|
||||
|
||||
pub const Syscall = enum(usize) {
|
||||
exit = 0,
|
||||
send = 1,
|
||||
receive = 2,
|
||||
subscribeIRQ = 3,
|
||||
inb = 4,
|
||||
outb = 5,
|
||||
map = 6,
|
||||
createThread = 7,
|
||||
exit = 0,
|
||||
send = 1,
|
||||
receive = 2,
|
||||
subscribeIRQ = 3,
|
||||
inb = 4,
|
||||
outb = 5,
|
||||
map = 6,
|
||||
createThread = 7,
|
||||
};
|
||||
|
||||
////////////////////
|
||||
|
||||
646
std/pdb.zig
Normal file
646
std/pdb.zig
Normal file
@ -0,0 +1,646 @@
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("index.zig");
|
||||
const io = std.io;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const os = std.os;
|
||||
const warn = std.debug.warn;
|
||||
const coff = std.coff;
|
||||
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
// https://llvm.org/docs/PDB/DbiStream.html#stream-header
|
||||
pub const DbiStreamHeader = packed struct {
|
||||
VersionSignature: i32,
|
||||
VersionHeader: u32,
|
||||
Age: u32,
|
||||
GlobalStreamIndex: u16,
|
||||
BuildNumber: u16,
|
||||
PublicStreamIndex: u16,
|
||||
PdbDllVersion: u16,
|
||||
SymRecordStream: u16,
|
||||
PdbDllRbld: u16,
|
||||
ModInfoSize: u32,
|
||||
SectionContributionSize: u32,
|
||||
SectionMapSize: u32,
|
||||
SourceInfoSize: i32,
|
||||
TypeServerSize: i32,
|
||||
MFCTypeServerIndex: u32,
|
||||
OptionalDbgHeaderSize: i32,
|
||||
ECSubstreamSize: i32,
|
||||
Flags: u16,
|
||||
Machine: u16,
|
||||
Padding: u32,
|
||||
};
|
||||
|
||||
pub const SectionContribEntry = packed struct {
|
||||
Section: u16,
|
||||
Padding1: [2]u8,
|
||||
Offset: u32,
|
||||
Size: u32,
|
||||
Characteristics: u32,
|
||||
ModuleIndex: u16,
|
||||
Padding2: [2]u8,
|
||||
DataCrc: u32,
|
||||
RelocCrc: u32,
|
||||
};
|
||||
|
||||
pub const ModInfo = packed struct {
|
||||
Unused1: u32,
|
||||
SectionContr: SectionContribEntry,
|
||||
Flags: u16,
|
||||
ModuleSymStream: u16,
|
||||
SymByteSize: u32,
|
||||
C11ByteSize: u32,
|
||||
C13ByteSize: u32,
|
||||
SourceFileCount: u16,
|
||||
Padding: [2]u8,
|
||||
Unused2: u32,
|
||||
SourceFileNameIndex: u32,
|
||||
PdbFilePathNameIndex: u32,
|
||||
// These fields are variable length
|
||||
//ModuleName: char[],
|
||||
//ObjFileName: char[],
|
||||
};
|
||||
|
||||
pub const SectionMapHeader = packed struct {
|
||||
Count: u16, /// Number of segment descriptors
|
||||
LogCount: u16, /// Number of logical segment descriptors
|
||||
};
|
||||
|
||||
pub const SectionMapEntry = packed struct {
|
||||
Flags: u16 , /// See the SectionMapEntryFlags enum below.
|
||||
Ovl: u16 , /// Logical overlay number
|
||||
Group: u16 , /// Group index into descriptor array.
|
||||
Frame: u16 ,
|
||||
SectionName: u16 , /// Byte index of segment / group name in string table, or 0xFFFF.
|
||||
ClassName: u16 , /// Byte index of class in string table, or 0xFFFF.
|
||||
Offset: u32 , /// Byte offset of the logical segment within physical segment. If group is set in flags, this is the offset of the group.
|
||||
SectionLength: u32 , /// Byte count of the segment or group.
|
||||
};
|
||||
|
||||
pub const StreamType = enum(u16) {
|
||||
Pdb = 1,
|
||||
Tpi = 2,
|
||||
Dbi = 3,
|
||||
Ipi = 4,
|
||||
};
|
||||
|
||||
/// Duplicate copy of SymbolRecordKind, but using the official CV names. Useful
|
||||
/// for reference purposes and when dealing with unknown record types.
|
||||
pub const SymbolKind = packed enum(u16) {
|
||||
S_COMPILE = 1,
|
||||
S_REGISTER_16t = 2,
|
||||
S_CONSTANT_16t = 3,
|
||||
S_UDT_16t = 4,
|
||||
S_SSEARCH = 5,
|
||||
S_SKIP = 7,
|
||||
S_CVRESERVE = 8,
|
||||
S_OBJNAME_ST = 9,
|
||||
S_ENDARG = 10,
|
||||
S_COBOLUDT_16t = 11,
|
||||
S_MANYREG_16t = 12,
|
||||
S_RETURN = 13,
|
||||
S_ENTRYTHIS = 14,
|
||||
S_BPREL16 = 256,
|
||||
S_LDATA16 = 257,
|
||||
S_GDATA16 = 258,
|
||||
S_PUB16 = 259,
|
||||
S_LPROC16 = 260,
|
||||
S_GPROC16 = 261,
|
||||
S_THUNK16 = 262,
|
||||
S_BLOCK16 = 263,
|
||||
S_WITH16 = 264,
|
||||
S_LABEL16 = 265,
|
||||
S_CEXMODEL16 = 266,
|
||||
S_VFTABLE16 = 267,
|
||||
S_REGREL16 = 268,
|
||||
S_BPREL32_16t = 512,
|
||||
S_LDATA32_16t = 513,
|
||||
S_GDATA32_16t = 514,
|
||||
S_PUB32_16t = 515,
|
||||
S_LPROC32_16t = 516,
|
||||
S_GPROC32_16t = 517,
|
||||
S_THUNK32_ST = 518,
|
||||
S_BLOCK32_ST = 519,
|
||||
S_WITH32_ST = 520,
|
||||
S_LABEL32_ST = 521,
|
||||
S_CEXMODEL32 = 522,
|
||||
S_VFTABLE32_16t = 523,
|
||||
S_REGREL32_16t = 524,
|
||||
S_LTHREAD32_16t = 525,
|
||||
S_GTHREAD32_16t = 526,
|
||||
S_SLINK32 = 527,
|
||||
S_LPROCMIPS_16t = 768,
|
||||
S_GPROCMIPS_16t = 769,
|
||||
S_PROCREF_ST = 1024,
|
||||
S_DATAREF_ST = 1025,
|
||||
S_ALIGN = 1026,
|
||||
S_LPROCREF_ST = 1027,
|
||||
S_OEM = 1028,
|
||||
S_TI16_MAX = 4096,
|
||||
S_REGISTER_ST = 4097,
|
||||
S_CONSTANT_ST = 4098,
|
||||
S_UDT_ST = 4099,
|
||||
S_COBOLUDT_ST = 4100,
|
||||
S_MANYREG_ST = 4101,
|
||||
S_BPREL32_ST = 4102,
|
||||
S_LDATA32_ST = 4103,
|
||||
S_GDATA32_ST = 4104,
|
||||
S_PUB32_ST = 4105,
|
||||
S_LPROC32_ST = 4106,
|
||||
S_GPROC32_ST = 4107,
|
||||
S_VFTABLE32 = 4108,
|
||||
S_REGREL32_ST = 4109,
|
||||
S_LTHREAD32_ST = 4110,
|
||||
S_GTHREAD32_ST = 4111,
|
||||
S_LPROCMIPS_ST = 4112,
|
||||
S_GPROCMIPS_ST = 4113,
|
||||
S_COMPILE2_ST = 4115,
|
||||
S_MANYREG2_ST = 4116,
|
||||
S_LPROCIA64_ST = 4117,
|
||||
S_GPROCIA64_ST = 4118,
|
||||
S_LOCALSLOT_ST = 4119,
|
||||
S_PARAMSLOT_ST = 4120,
|
||||
S_ANNOTATION = 4121,
|
||||
S_GMANPROC_ST = 4122,
|
||||
S_LMANPROC_ST = 4123,
|
||||
S_RESERVED1 = 4124,
|
||||
S_RESERVED2 = 4125,
|
||||
S_RESERVED3 = 4126,
|
||||
S_RESERVED4 = 4127,
|
||||
S_LMANDATA_ST = 4128,
|
||||
S_GMANDATA_ST = 4129,
|
||||
S_MANFRAMEREL_ST = 4130,
|
||||
S_MANREGISTER_ST = 4131,
|
||||
S_MANSLOT_ST = 4132,
|
||||
S_MANMANYREG_ST = 4133,
|
||||
S_MANREGREL_ST = 4134,
|
||||
S_MANMANYREG2_ST = 4135,
|
||||
S_MANTYPREF = 4136,
|
||||
S_UNAMESPACE_ST = 4137,
|
||||
S_ST_MAX = 4352,
|
||||
S_WITH32 = 4356,
|
||||
S_MANYREG = 4362,
|
||||
S_LPROCMIPS = 4372,
|
||||
S_GPROCMIPS = 4373,
|
||||
S_MANYREG2 = 4375,
|
||||
S_LPROCIA64 = 4376,
|
||||
S_GPROCIA64 = 4377,
|
||||
S_LOCALSLOT = 4378,
|
||||
S_PARAMSLOT = 4379,
|
||||
S_MANFRAMEREL = 4382,
|
||||
S_MANREGISTER = 4383,
|
||||
S_MANSLOT = 4384,
|
||||
S_MANMANYREG = 4385,
|
||||
S_MANREGREL = 4386,
|
||||
S_MANMANYREG2 = 4387,
|
||||
S_UNAMESPACE = 4388,
|
||||
S_DATAREF = 4390,
|
||||
S_ANNOTATIONREF = 4392,
|
||||
S_TOKENREF = 4393,
|
||||
S_GMANPROC = 4394,
|
||||
S_LMANPROC = 4395,
|
||||
S_ATTR_FRAMEREL = 4398,
|
||||
S_ATTR_REGISTER = 4399,
|
||||
S_ATTR_REGREL = 4400,
|
||||
S_ATTR_MANYREG = 4401,
|
||||
S_SEPCODE = 4402,
|
||||
S_LOCAL_2005 = 4403,
|
||||
S_DEFRANGE_2005 = 4404,
|
||||
S_DEFRANGE2_2005 = 4405,
|
||||
S_DISCARDED = 4411,
|
||||
S_LPROCMIPS_ID = 4424,
|
||||
S_GPROCMIPS_ID = 4425,
|
||||
S_LPROCIA64_ID = 4426,
|
||||
S_GPROCIA64_ID = 4427,
|
||||
S_DEFRANGE_HLSL = 4432,
|
||||
S_GDATA_HLSL = 4433,
|
||||
S_LDATA_HLSL = 4434,
|
||||
S_LOCAL_DPC_GROUPSHARED = 4436,
|
||||
S_DEFRANGE_DPC_PTR_TAG = 4439,
|
||||
S_DPC_SYM_TAG_MAP = 4440,
|
||||
S_ARMSWITCHTABLE = 4441,
|
||||
S_POGODATA = 4444,
|
||||
S_INLINESITE2 = 4445,
|
||||
S_MOD_TYPEREF = 4447,
|
||||
S_REF_MINIPDB = 4448,
|
||||
S_PDBMAP = 4449,
|
||||
S_GDATA_HLSL32 = 4450,
|
||||
S_LDATA_HLSL32 = 4451,
|
||||
S_GDATA_HLSL32_EX = 4452,
|
||||
S_LDATA_HLSL32_EX = 4453,
|
||||
S_FASTLINK = 4455,
|
||||
S_INLINEES = 4456,
|
||||
S_END = 6,
|
||||
S_INLINESITE_END = 4430,
|
||||
S_PROC_ID_END = 4431,
|
||||
S_THUNK32 = 4354,
|
||||
S_TRAMPOLINE = 4396,
|
||||
S_SECTION = 4406,
|
||||
S_COFFGROUP = 4407,
|
||||
S_EXPORT = 4408,
|
||||
S_LPROC32 = 4367,
|
||||
S_GPROC32 = 4368,
|
||||
S_LPROC32_ID = 4422,
|
||||
S_GPROC32_ID = 4423,
|
||||
S_LPROC32_DPC = 4437,
|
||||
S_LPROC32_DPC_ID = 4438,
|
||||
S_REGISTER = 4358,
|
||||
S_PUB32 = 4366,
|
||||
S_PROCREF = 4389,
|
||||
S_LPROCREF = 4391,
|
||||
S_ENVBLOCK = 4413,
|
||||
S_INLINESITE = 4429,
|
||||
S_LOCAL = 4414,
|
||||
S_DEFRANGE = 4415,
|
||||
S_DEFRANGE_SUBFIELD = 4416,
|
||||
S_DEFRANGE_REGISTER = 4417,
|
||||
S_DEFRANGE_FRAMEPOINTER_REL = 4418,
|
||||
S_DEFRANGE_SUBFIELD_REGISTER = 4419,
|
||||
S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE = 4420,
|
||||
S_DEFRANGE_REGISTER_REL = 4421,
|
||||
S_BLOCK32 = 4355,
|
||||
S_LABEL32 = 4357,
|
||||
S_OBJNAME = 4353,
|
||||
S_COMPILE2 = 4374,
|
||||
S_COMPILE3 = 4412,
|
||||
S_FRAMEPROC = 4114,
|
||||
S_CALLSITEINFO = 4409,
|
||||
S_FILESTATIC = 4435,
|
||||
S_HEAPALLOCSITE = 4446,
|
||||
S_FRAMECOOKIE = 4410,
|
||||
S_CALLEES = 4442,
|
||||
S_CALLERS = 4443,
|
||||
S_UDT = 4360,
|
||||
S_COBOLUDT = 4361,
|
||||
S_BUILDINFO = 4428,
|
||||
S_BPREL32 = 4363,
|
||||
S_REGREL32 = 4369,
|
||||
S_CONSTANT = 4359,
|
||||
S_MANCONSTANT = 4397,
|
||||
S_LDATA32 = 4364,
|
||||
S_GDATA32 = 4365,
|
||||
S_LMANDATA = 4380,
|
||||
S_GMANDATA = 4381,
|
||||
S_LTHREAD32 = 4370,
|
||||
S_GTHREAD32 = 4371,
|
||||
};
|
||||
|
||||
pub const TypeIndex = u32;
|
||||
|
||||
pub const ProcSym = packed struct {
|
||||
Parent: u32 ,
|
||||
End: u32 ,
|
||||
Next: u32 ,
|
||||
CodeSize: u32 ,
|
||||
DbgStart: u32 ,
|
||||
DbgEnd: u32 ,
|
||||
FunctionType: TypeIndex ,
|
||||
CodeOffset: u32,
|
||||
Segment: u16,
|
||||
Flags: ProcSymFlags,
|
||||
// following is a null terminated string
|
||||
// Name: [*]u8,
|
||||
};
|
||||
|
||||
pub const ProcSymFlags = packed struct {
|
||||
HasFP: bool,
|
||||
HasIRET: bool,
|
||||
HasFRET: bool,
|
||||
IsNoReturn: bool,
|
||||
IsUnreachable: bool,
|
||||
HasCustomCallingConv: bool,
|
||||
IsNoInline: bool,
|
||||
HasOptimizedDebugInfo: bool,
|
||||
};
|
||||
|
||||
pub const SectionContrSubstreamVersion = enum(u32) {
|
||||
Ver60 = 0xeffe0000 + 19970605,
|
||||
V2 = 0xeffe0000 + 20140516
|
||||
};
|
||||
|
||||
pub const RecordPrefix = packed struct {
|
||||
RecordLen: u16, /// Record length, starting from &RecordKind.
|
||||
RecordKind: SymbolKind, /// Record kind enum (SymRecordKind or TypeRecordKind)
|
||||
};
|
||||
|
||||
pub const LineFragmentHeader = packed struct {
|
||||
RelocOffset: u32, /// Code offset of line contribution.
|
||||
RelocSegment: u16, /// Code segment of line contribution.
|
||||
Flags: LineFlags,
|
||||
CodeSize: u32, /// Code size of this line contribution.
|
||||
};
|
||||
|
||||
pub const LineFlags = packed struct {
|
||||
LF_HaveColumns: bool, /// CV_LINES_HAVE_COLUMNS
|
||||
unused: u15,
|
||||
};
|
||||
|
||||
/// The following two variable length arrays appear immediately after the
|
||||
/// header. The structure definitions follow.
|
||||
/// LineNumberEntry Lines[NumLines];
|
||||
/// ColumnNumberEntry Columns[NumLines];
|
||||
pub const LineBlockFragmentHeader = packed struct {
|
||||
/// Offset of FileChecksum entry in File
|
||||
/// checksums buffer. The checksum entry then
|
||||
/// contains another offset into the string
|
||||
/// table of the actual name.
|
||||
NameIndex: u32,
|
||||
NumLines: u32,
|
||||
BlockSize: u32, /// code size of block, in bytes
|
||||
};
|
||||
|
||||
|
||||
pub const LineNumberEntry = packed struct {
|
||||
Offset: u32, /// Offset to start of code bytes for line number
|
||||
Flags: u32,
|
||||
|
||||
/// TODO runtime crash when I make the actual type of Flags this
|
||||
const Flags = packed struct {
|
||||
Start: u24,
|
||||
End: u7,
|
||||
IsStatement: bool,
|
||||
};
|
||||
};
|
||||
|
||||
pub const ColumnNumberEntry = packed struct {
|
||||
StartColumn: u16,
|
||||
EndColumn: u16,
|
||||
};
|
||||
|
||||
/// Checksum bytes follow.
|
||||
pub const FileChecksumEntryHeader = packed struct {
|
||||
FileNameOffset: u32, /// Byte offset of filename in global string table.
|
||||
ChecksumSize: u8, /// Number of bytes of checksum.
|
||||
ChecksumKind: u8, /// FileChecksumKind
|
||||
};
|
||||
|
||||
pub const DebugSubsectionKind = packed enum(u32) {
|
||||
None = 0,
|
||||
Symbols = 0xf1,
|
||||
Lines = 0xf2,
|
||||
StringTable = 0xf3,
|
||||
FileChecksums = 0xf4,
|
||||
FrameData = 0xf5,
|
||||
InlineeLines = 0xf6,
|
||||
CrossScopeImports = 0xf7,
|
||||
CrossScopeExports = 0xf8,
|
||||
|
||||
// These appear to relate to .Net assembly info.
|
||||
ILLines = 0xf9,
|
||||
FuncMDTokenMap = 0xfa,
|
||||
TypeMDTokenMap = 0xfb,
|
||||
MergedAssemblyInput = 0xfc,
|
||||
|
||||
CoffSymbolRVA = 0xfd,
|
||||
};
|
||||
|
||||
|
||||
pub const DebugSubsectionHeader = packed struct {
|
||||
Kind: DebugSubsectionKind, /// codeview::DebugSubsectionKind enum
|
||||
Length: u32, /// number of bytes occupied by this record.
|
||||
};
|
||||
|
||||
|
||||
pub const PDBStringTableHeader = packed struct {
|
||||
Signature: u32, /// PDBStringTableSignature
|
||||
HashVersion: u32, /// 1 or 2
|
||||
ByteSize: u32, /// Number of bytes of names buffer.
|
||||
};
|
||||
|
||||
pub const Pdb = struct {
|
||||
in_file: os.File,
|
||||
allocator: *mem.Allocator,
|
||||
coff: *coff.Coff,
|
||||
string_table: *MsfStream,
|
||||
dbi: *MsfStream,
|
||||
|
||||
msf: Msf,
|
||||
|
||||
pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void {
|
||||
self.in_file = try os.File.openRead(file_name);
|
||||
self.allocator = coff_ptr.allocator;
|
||||
self.coff = coff_ptr;
|
||||
|
||||
try self.msf.openFile(self.allocator, self.in_file);
|
||||
}
|
||||
|
||||
pub fn getStreamById(self: *Pdb, id: u32) ?*MsfStream {
|
||||
if (id >= self.msf.streams.len)
|
||||
return null;
|
||||
return &self.msf.streams[id];
|
||||
}
|
||||
|
||||
pub fn getStream(self: *Pdb, stream: StreamType) ?*MsfStream {
|
||||
const id = @enumToInt(stream);
|
||||
return self.getStreamById(id);
|
||||
}
|
||||
};
|
||||
|
||||
// see https://llvm.org/docs/PDB/MsfFile.html
|
||||
const Msf = struct {
|
||||
directory: MsfStream,
|
||||
streams: []MsfStream,
|
||||
|
||||
fn openFile(self: *Msf, allocator: *mem.Allocator, file: os.File) !void {
|
||||
var file_stream = io.FileInStream.init(file);
|
||||
const in = &file_stream.stream;
|
||||
|
||||
var superblock: SuperBlock = undefined;
|
||||
try in.readStruct(SuperBlock, &superblock);
|
||||
|
||||
if (!mem.eql(u8, superblock.FileMagic, SuperBlock.file_magic))
|
||||
return error.InvalidDebugInfo;
|
||||
|
||||
switch (superblock.BlockSize) {
|
||||
// llvm only supports 4096 but we can handle any of these values
|
||||
512, 1024, 2048, 4096 => {},
|
||||
else => return error.InvalidDebugInfo
|
||||
}
|
||||
|
||||
if (superblock.NumBlocks * superblock.BlockSize != try file.getEndPos())
|
||||
return error.InvalidDebugInfo;
|
||||
|
||||
self.directory = try MsfStream.init(
|
||||
superblock.BlockSize,
|
||||
blockCountFromSize(superblock.NumDirectoryBytes, superblock.BlockSize),
|
||||
superblock.BlockSize * superblock.BlockMapAddr,
|
||||
file,
|
||||
allocator,
|
||||
);
|
||||
|
||||
const stream_count = try self.directory.stream.readIntLe(u32);
|
||||
|
||||
const stream_sizes = try allocator.alloc(u32, stream_count);
|
||||
for (stream_sizes) |*s| {
|
||||
const size = try self.directory.stream.readIntLe(u32);
|
||||
s.* = blockCountFromSize(size, superblock.BlockSize);
|
||||
}
|
||||
|
||||
self.streams = try allocator.alloc(MsfStream, stream_count);
|
||||
for (self.streams) |*stream, i| {
|
||||
stream.* = try MsfStream.init(
|
||||
superblock.BlockSize,
|
||||
stream_sizes[i],
|
||||
// MsfStream.init expects the file to be at the part where it reads [N]u32
|
||||
try file.getPos(),
|
||||
file,
|
||||
allocator,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn blockCountFromSize(size: u32, block_size: u32) u32 {
|
||||
return (size + block_size - 1) / block_size;
|
||||
}
|
||||
|
||||
// https://llvm.org/docs/PDB/MsfFile.html#the-superblock
|
||||
const SuperBlock = packed struct {
|
||||
/// The LLVM docs list a space between C / C++ but empirically this is not the case.
|
||||
const file_magic = "Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53\x00\x00\x00";
|
||||
|
||||
FileMagic: [file_magic.len]u8,
|
||||
|
||||
/// The block size of the internal file system. Valid values are 512, 1024,
|
||||
/// 2048, and 4096 bytes. Certain aspects of the MSF file layout vary depending
|
||||
/// on the block sizes. For the purposes of LLVM, we handle only block sizes of
|
||||
/// 4KiB, and all further discussion assumes a block size of 4KiB.
|
||||
BlockSize: u32,
|
||||
|
||||
/// The index of a block within the file, at which begins a bitfield representing
|
||||
/// the set of all blocks within the file which are “free” (i.e. the data within
|
||||
/// that block is not used). See The Free Block Map for more information. Important:
|
||||
/// FreeBlockMapBlock can only be 1 or 2!
|
||||
FreeBlockMapBlock: u32,
|
||||
|
||||
/// The total number of blocks in the file. NumBlocks * BlockSize should equal the
|
||||
/// size of the file on disk.
|
||||
NumBlocks: u32,
|
||||
|
||||
/// The size of the stream directory, in bytes. The stream directory contains
|
||||
/// information about each stream’s size and the set of blocks that it occupies.
|
||||
/// It will be described in more detail later.
|
||||
NumDirectoryBytes: u32,
|
||||
|
||||
Unknown: u32,
|
||||
|
||||
/// The index of a block within the MSF file. At this block is an array of
|
||||
/// ulittle32_t’s listing the blocks that the stream directory resides on.
|
||||
/// For large MSF files, the stream directory (which describes the block
|
||||
/// layout of each stream) may not fit entirely on a single block. As a
|
||||
/// result, this extra layer of indirection is introduced, whereby this
|
||||
/// block contains the list of blocks that the stream directory occupies,
|
||||
/// and the stream directory itself can be stitched together accordingly.
|
||||
/// The number of ulittle32_t’s in this array is given by
|
||||
/// ceil(NumDirectoryBytes / BlockSize).
|
||||
BlockMapAddr: u32,
|
||||
|
||||
};
|
||||
|
||||
const MsfStream = struct {
|
||||
in_file: os.File,
|
||||
pos: usize,
|
||||
blocks: []u32,
|
||||
block_size: u32,
|
||||
|
||||
/// Implementation of InStream trait for Pdb.MsfStream
|
||||
stream: Stream,
|
||||
|
||||
pub const Error = @typeOf(read).ReturnType.ErrorSet;
|
||||
pub const Stream = io.InStream(Error);
|
||||
|
||||
fn init(block_size: u32, block_count: u32, pos: usize, file: os.File, allocator: *mem.Allocator) !MsfStream {
|
||||
var stream = MsfStream {
|
||||
.in_file = file,
|
||||
.pos = 0,
|
||||
.blocks = try allocator.alloc(u32, block_count),
|
||||
.block_size = block_size,
|
||||
.stream = Stream {
|
||||
.readFn = readFn,
|
||||
},
|
||||
};
|
||||
|
||||
var file_stream = io.FileInStream.init(file);
|
||||
const in = &file_stream.stream;
|
||||
try file.seekTo(pos);
|
||||
|
||||
var i: u32 = 0;
|
||||
while (i < block_count) : (i += 1) {
|
||||
stream.blocks[i] = try in.readIntLe(u32);
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 {
|
||||
var list = ArrayList(u8).init(allocator);
|
||||
defer list.deinit();
|
||||
while (true) {
|
||||
const byte = try self.stream.readByte();
|
||||
if (byte == 0) {
|
||||
return list.toSlice();
|
||||
}
|
||||
try list.append(byte);
|
||||
}
|
||||
}
|
||||
|
||||
fn read(self: *MsfStream, buffer: []u8) !usize {
|
||||
var block_id = self.pos / self.block_size;
|
||||
var block = self.blocks[block_id];
|
||||
var offset = self.pos % self.block_size;
|
||||
|
||||
try self.in_file.seekTo(block * self.block_size + offset);
|
||||
var file_stream = io.FileInStream.init(self.in_file);
|
||||
const in = &file_stream.stream;
|
||||
|
||||
var size: usize = 0;
|
||||
for (buffer) |*byte| {
|
||||
byte.* = try in.readByte();
|
||||
|
||||
offset += 1;
|
||||
size += 1;
|
||||
|
||||
// If we're at the end of a block, go to the next one.
|
||||
if (offset == self.block_size) {
|
||||
offset = 0;
|
||||
block_id += 1;
|
||||
block = self.blocks[block_id];
|
||||
try self.in_file.seekTo(block * self.block_size);
|
||||
}
|
||||
}
|
||||
|
||||
self.pos += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
fn seekForward(self: *MsfStream, len: usize) !void {
|
||||
self.pos += len;
|
||||
if (self.pos >= self.blocks.len * self.block_size)
|
||||
return error.EOF;
|
||||
}
|
||||
|
||||
fn seekTo(self: *MsfStream, len: usize) !void {
|
||||
self.pos = len;
|
||||
if (self.pos >= self.blocks.len * self.block_size)
|
||||
return error.EOF;
|
||||
}
|
||||
|
||||
fn getSize(self: *const MsfStream) usize {
|
||||
return self.blocks.len * self.block_size;
|
||||
}
|
||||
|
||||
fn getFilePos(self: MsfStream) usize {
|
||||
const block_id = self.pos / self.block_size;
|
||||
const block = self.blocks[block_id];
|
||||
const offset = self.pos % self.block_size;
|
||||
|
||||
return block * self.block_size + offset;
|
||||
}
|
||||
|
||||
fn readFn(in_stream: *Stream, buffer: []u8) Error!usize {
|
||||
const self = @fieldParentPtr(MsfStream, "stream", in_stream);
|
||||
return self.read(buffer);
|
||||
}
|
||||
};
|
||||
30
std/rb.zig
30
std/rb.zig
@ -9,9 +9,7 @@ const Color = enum(u1) {
|
||||
const Red = Color.Red;
|
||||
const Black = Color.Black;
|
||||
|
||||
const ReplaceError = error {
|
||||
NotEqual,
|
||||
};
|
||||
const ReplaceError = error{NotEqual};
|
||||
|
||||
/// Insert this into your struct that you want to add to a red-black tree.
|
||||
/// Do not use a pointer. Turn the *rb.Node results of the functions in rb
|
||||
@ -21,13 +19,15 @@ const ReplaceError = error {
|
||||
/// node: rb.Node,
|
||||
/// value: i32,
|
||||
/// };
|
||||
/// fn number(node: *Node) Number {
|
||||
/// fn number(node: *rb.Node) Number {
|
||||
/// return @fieldParentPtr(Number, "node", node);
|
||||
/// }
|
||||
pub const Node = struct {
|
||||
left: ?*Node,
|
||||
right: ?*Node,
|
||||
parent_and_color: usize, /// parent | color
|
||||
|
||||
/// parent | color
|
||||
parent_and_color: usize,
|
||||
|
||||
pub fn next(constnode: *Node) ?*Node {
|
||||
var node = constnode;
|
||||
@ -130,7 +130,7 @@ pub const Node = struct {
|
||||
|
||||
pub const Tree = struct {
|
||||
root: ?*Node,
|
||||
compareFn: fn(*Node, *Node) mem.Compare,
|
||||
compareFn: fn (*Node, *Node) mem.Compare,
|
||||
|
||||
/// If you have a need for a version that caches this, please file a bug.
|
||||
pub fn first(tree: *Tree) ?*Node {
|
||||
@ -180,7 +180,7 @@ pub const Tree = struct {
|
||||
while (node.get_parent()) |*parent| {
|
||||
if (parent.*.is_black())
|
||||
break;
|
||||
// the root is always black
|
||||
// the root is always black
|
||||
var grandpa = parent.*.get_parent() orelse unreachable;
|
||||
|
||||
if (parent.* == grandpa.left) {
|
||||
@ -206,7 +206,7 @@ pub const Tree = struct {
|
||||
}
|
||||
} else {
|
||||
var maybe_uncle = grandpa.left;
|
||||
|
||||
|
||||
if (maybe_uncle) |uncle| {
|
||||
if (uncle.is_black())
|
||||
break;
|
||||
@ -259,7 +259,7 @@ pub const Tree = struct {
|
||||
if (node.left == null) {
|
||||
next = node.right.?; // Not both null as per above
|
||||
} else if (node.right == null) {
|
||||
next = node.left.?; // Not both null as per above
|
||||
next = node.left.?; // Not both null as per above
|
||||
} else
|
||||
next = node.right.?.get_first(); // Just checked for null above
|
||||
|
||||
@ -313,7 +313,7 @@ pub const Tree = struct {
|
||||
var parent = maybe_parent.?;
|
||||
if (node == parent.left) {
|
||||
var sibling = parent.right.?; // Same number of black nodes.
|
||||
|
||||
|
||||
if (sibling.is_red()) {
|
||||
sibling.set_color(Black);
|
||||
parent.set_color(Red);
|
||||
@ -321,7 +321,8 @@ pub const Tree = struct {
|
||||
sibling = parent.right.?; // Just rotated
|
||||
}
|
||||
if ((if (sibling.left) |n| n.is_black() else true) and
|
||||
(if (sibling.right) |n| n.is_black() else true)) {
|
||||
(if (sibling.right) |n| n.is_black() else true))
|
||||
{
|
||||
sibling.set_color(Red);
|
||||
node = parent;
|
||||
maybe_parent = parent.get_parent();
|
||||
@ -341,7 +342,7 @@ pub const Tree = struct {
|
||||
break;
|
||||
} else {
|
||||
var sibling = parent.left.?; // Same number of black nodes.
|
||||
|
||||
|
||||
if (sibling.is_red()) {
|
||||
sibling.set_color(Black);
|
||||
parent.set_color(Red);
|
||||
@ -349,7 +350,8 @@ pub const Tree = struct {
|
||||
sibling = parent.left.?; // Just rotated
|
||||
}
|
||||
if ((if (sibling.left) |n| n.is_black() else true) and
|
||||
(if (sibling.right) |n| n.is_black() else true)) {
|
||||
(if (sibling.right) |n| n.is_black() else true))
|
||||
{
|
||||
sibling.set_color(Red);
|
||||
node = parent;
|
||||
maybe_parent = parent.get_parent();
|
||||
@ -397,7 +399,7 @@ pub const Tree = struct {
|
||||
new.* = old.*;
|
||||
}
|
||||
|
||||
pub fn init(tree: *Tree, f: fn(*Node, *Node) mem.Compare) void {
|
||||
pub fn init(tree: *Tree, f: fn (*Node, *Node) mem.Compare) void {
|
||||
tree.root = null;
|
||||
tree.compareFn = f;
|
||||
}
|
||||
|
||||
@ -49,14 +49,14 @@ pub fn main() !void {
|
||||
|
||||
var stderr_file = io.getStdErr();
|
||||
var stderr_file_stream: io.FileOutStream = undefined;
|
||||
var stderr_stream = if (stderr_file) |*f| x: {
|
||||
var stderr_stream = if (stderr_file) |f| x: {
|
||||
stderr_file_stream = io.FileOutStream.init(f);
|
||||
break :x &stderr_file_stream.stream;
|
||||
} else |err| err;
|
||||
|
||||
var stdout_file = io.getStdOut();
|
||||
var stdout_file_stream: io.FileOutStream = undefined;
|
||||
var stdout_stream = if (stdout_file) |*f| x: {
|
||||
var stdout_stream = if (stdout_file) |f| x: {
|
||||
stdout_file_stream = io.FileOutStream.init(f);
|
||||
break :x &stdout_file_stream.stream;
|
||||
} else |err| err;
|
||||
|
||||
@ -340,7 +340,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
|
||||
const node_ptr = try ctx.container_decl.fields_and_decls.addOne();
|
||||
node_ptr.* = &node.base;
|
||||
|
||||
stack.append(State{ .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable;
|
||||
try stack.append(State{
|
||||
.FieldListCommaOrEnd = FieldCtx{
|
||||
.doc_comments = &node.doc_comments,
|
||||
.container_decl = ctx.container_decl,
|
||||
},
|
||||
});
|
||||
try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.type_expr } });
|
||||
try stack.append(State{ .ExpectToken = Token.Id.Colon });
|
||||
continue;
|
||||
@ -458,7 +463,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
|
||||
const node_ptr = try container_decl.fields_and_decls.addOne();
|
||||
node_ptr.* = &node.base;
|
||||
|
||||
try stack.append(State{ .FieldListCommaOrEnd = container_decl });
|
||||
try stack.append(State{
|
||||
.FieldListCommaOrEnd = FieldCtx{
|
||||
.doc_comments = &node.doc_comments,
|
||||
.container_decl = container_decl,
|
||||
},
|
||||
});
|
||||
try stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &node.type_expr } });
|
||||
try stack.append(State{ .ExpectToken = Token.Id.Colon });
|
||||
continue;
|
||||
@ -473,7 +483,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
|
||||
});
|
||||
try container_decl.fields_and_decls.push(&node.base);
|
||||
|
||||
stack.append(State{ .FieldListCommaOrEnd = container_decl }) catch unreachable;
|
||||
try stack.append(State{
|
||||
.FieldListCommaOrEnd = FieldCtx{
|
||||
.doc_comments = &node.doc_comments,
|
||||
.container_decl = container_decl,
|
||||
},
|
||||
});
|
||||
try stack.append(State{ .FieldInitValue = OptionalCtx{ .RequiredNull = &node.value_expr } });
|
||||
try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &node.type_expr } });
|
||||
try stack.append(State{ .IfToken = Token.Id.Colon });
|
||||
@ -488,7 +503,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
|
||||
});
|
||||
try container_decl.fields_and_decls.push(&node.base);
|
||||
|
||||
stack.append(State{ .FieldListCommaOrEnd = container_decl }) catch unreachable;
|
||||
try stack.append(State{
|
||||
.FieldListCommaOrEnd = FieldCtx{
|
||||
.doc_comments = &node.doc_comments,
|
||||
.container_decl = container_decl,
|
||||
},
|
||||
});
|
||||
try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &node.value } });
|
||||
try stack.append(State{ .IfToken = Token.Id.Equal });
|
||||
continue;
|
||||
@ -1265,17 +1285,35 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
|
||||
},
|
||||
}
|
||||
},
|
||||
State.FieldListCommaOrEnd => |container_decl| {
|
||||
switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) {
|
||||
ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
|
||||
container_decl.rbrace_token = end;
|
||||
continue;
|
||||
} else {
|
||||
try stack.append(State{ .ContainerDecl = container_decl });
|
||||
State.FieldListCommaOrEnd => |field_ctx| {
|
||||
const end_token = nextToken(&tok_it, &tree);
|
||||
const end_token_index = end_token.index;
|
||||
const end_token_ptr = end_token.ptr;
|
||||
switch (end_token_ptr.id) {
|
||||
Token.Id.Comma => {
|
||||
if (eatToken(&tok_it, &tree, Token.Id.DocComment)) |doc_comment_token| {
|
||||
const loc = tree.tokenLocation(end_token_ptr.end, doc_comment_token);
|
||||
if (loc.line == 0) {
|
||||
try pushDocComment(arena, doc_comment_token, field_ctx.doc_comments);
|
||||
} else {
|
||||
prevToken(&tok_it, &tree);
|
||||
}
|
||||
}
|
||||
|
||||
try stack.append(State{ .ContainerDecl = field_ctx.container_decl });
|
||||
continue;
|
||||
},
|
||||
ExpectCommaOrEndResult.parse_error => |e| {
|
||||
try tree.errors.push(e);
|
||||
Token.Id.RBrace => {
|
||||
field_ctx.container_decl.rbrace_token = end_token_index;
|
||||
continue;
|
||||
},
|
||||
else => {
|
||||
try tree.errors.push(Error{
|
||||
.ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd{
|
||||
.token = end_token_index,
|
||||
.end_id = end_token_ptr.id,
|
||||
},
|
||||
});
|
||||
return tree;
|
||||
},
|
||||
}
|
||||
@ -2813,6 +2851,11 @@ const ExprListCtx = struct {
|
||||
ptr: *TokenIndex,
|
||||
};
|
||||
|
||||
const FieldCtx = struct {
|
||||
container_decl: *ast.Node.ContainerDecl,
|
||||
doc_comments: *?*ast.Node.DocComment,
|
||||
};
|
||||
|
||||
fn ListSave(comptime List: type) type {
|
||||
return struct {
|
||||
list: *List,
|
||||
@ -2950,7 +2993,7 @@ const State = union(enum) {
|
||||
ExprListCommaOrEnd: ExprListCtx,
|
||||
FieldInitListItemOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList),
|
||||
FieldInitListCommaOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList),
|
||||
FieldListCommaOrEnd: *ast.Node.ContainerDecl,
|
||||
FieldListCommaOrEnd: FieldCtx,
|
||||
FieldInitValue: OptionalCtx,
|
||||
ErrorTagListItemOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList),
|
||||
ErrorTagListCommaOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList),
|
||||
|
||||
@ -1,3 +1,20 @@
|
||||
test "zig fmt: correctly move doc comments on struct fields" {
|
||||
try testTransform(
|
||||
\\pub const section_64 = extern struct {
|
||||
\\ sectname: [16]u8, /// name of this section
|
||||
\\ segname: [16]u8, /// segment this section goes in
|
||||
\\};
|
||||
,
|
||||
\\pub const section_64 = extern struct {
|
||||
\\ /// name of this section
|
||||
\\ sectname: [16]u8,
|
||||
\\ /// segment this section goes in
|
||||
\\ segname: [16]u8,
|
||||
\\};
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: preserve space between async fn definitions" {
|
||||
try testCanonical(
|
||||
\\async fn a() void {}
|
||||
@ -1848,7 +1865,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined;
|
||||
|
||||
fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 {
|
||||
var stderr_file = try io.getStdErr();
|
||||
var stderr = &io.FileOutStream.init(&stderr_file).stream;
|
||||
var stderr = &io.FileOutStream.init(stderr_file).stream;
|
||||
|
||||
var tree = try std.zig.parse(allocator, source);
|
||||
defer tree.deinit();
|
||||
|
||||
@ -11,6 +11,7 @@ comptime {
|
||||
_ = @import("cases/bugs/1111.zig");
|
||||
_ = @import("cases/bugs/1230.zig");
|
||||
_ = @import("cases/bugs/1277.zig");
|
||||
_ = @import("cases/bugs/1421.zig");
|
||||
_ = @import("cases/bugs/394.zig");
|
||||
_ = @import("cases/bugs/655.zig");
|
||||
_ = @import("cases/bugs/656.zig");
|
||||
|
||||
14
test/cases/bugs/1421.zig
Normal file
14
test/cases/bugs/1421.zig
Normal file
@ -0,0 +1,14 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const S = struct {
|
||||
fn method() builtin.TypeInfo {
|
||||
return @typeInfo(S);
|
||||
}
|
||||
};
|
||||
|
||||
test "functions with return type required to be comptime are generic" {
|
||||
const ti = S.method();
|
||||
assert(builtin.TypeId(ti) == builtin.TypeId.Struct);
|
||||
}
|
||||
@ -487,12 +487,41 @@ fn MakeType(comptime T: type) type {
|
||||
}
|
||||
|
||||
test "implicit cast from *[N]T to ?[*]T" {
|
||||
var x: ?[*]u16 = null;
|
||||
var y: [4]u16 = [4]u16 {0, 1, 2, 3};
|
||||
var x: ?[*]u16 = null;
|
||||
var y: [4]u16 = [4]u16{ 0, 1, 2, 3 };
|
||||
|
||||
x = &y;
|
||||
assert(std.mem.eql(u16, x.?[0..4], y[0..4]));
|
||||
x.?[0] = 8;
|
||||
y[3] = 6;
|
||||
assert(std.mem.eql(u16, x.?[0..4], y[0..4]));
|
||||
}
|
||||
x = &y;
|
||||
assert(std.mem.eql(u16, x.?[0..4], y[0..4]));
|
||||
x.?[0] = 8;
|
||||
y[3] = 6;
|
||||
assert(std.mem.eql(u16, x.?[0..4], y[0..4]));
|
||||
}
|
||||
|
||||
test "implicit cast from *T to ?*c_void" {
|
||||
var a: u8 = 1;
|
||||
incrementVoidPtrValue(&a);
|
||||
std.debug.assert(a == 2);
|
||||
}
|
||||
|
||||
fn incrementVoidPtrValue(value: ?*c_void) void {
|
||||
@ptrCast(*u8, value.?).* += 1;
|
||||
}
|
||||
|
||||
test "implicit cast from [*]T to ?*c_void" {
|
||||
var a = []u8{ 3, 2, 1 };
|
||||
incrementVoidPtrArray(a[0..].ptr, 3);
|
||||
assert(std.mem.eql(u8, a, []u8{ 4, 3, 2 }));
|
||||
}
|
||||
|
||||
fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {
|
||||
var n: usize = 0;
|
||||
while (n < len) : (n += 1) {
|
||||
@ptrCast([*]u8, array.?)[n] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
test "*usize to *void" {
|
||||
var i = usize(0);
|
||||
var v = @ptrCast(*void, &i);
|
||||
v.* = {};
|
||||
}
|
||||
|
||||
@ -652,3 +652,25 @@ fn loopNTimes(comptime n: usize) void {
|
||||
comptime var i = 0;
|
||||
inline while (i < n) : (i += 1) {}
|
||||
}
|
||||
|
||||
test "variable inside inline loop that has different types on different iterations" {
|
||||
testVarInsideInlineLoop(true, u32(42));
|
||||
}
|
||||
|
||||
fn testVarInsideInlineLoop(args: ...) void {
|
||||
comptime var i = 0;
|
||||
inline while (i < args.len) : (i += 1) {
|
||||
const x = args[i];
|
||||
if (i == 0) assert(x);
|
||||
if (i == 1) assert(x == 42);
|
||||
}
|
||||
}
|
||||
|
||||
test "inline for with same type but different values" {
|
||||
var res: usize = 0;
|
||||
inline for ([]type{ [2]u8, [1]u8, [2]u8 }) |T| {
|
||||
var a: T = undefined;
|
||||
res += a.len;
|
||||
}
|
||||
assert(res == 5);
|
||||
}
|
||||
|
||||
@ -71,8 +71,7 @@ fn testBreakOuter() void {
|
||||
var array = "aoeu";
|
||||
var count: usize = 0;
|
||||
outer: for (array) |_| {
|
||||
// TODO shouldn't get error for redeclaring "_"
|
||||
for (array) |_2| {
|
||||
for (array) |_| {
|
||||
count += 1;
|
||||
break :outer;
|
||||
}
|
||||
@ -89,8 +88,7 @@ fn testContinueOuter() void {
|
||||
var array = "aoeu";
|
||||
var counter: usize = 0;
|
||||
outer: for (array) |_| {
|
||||
// TODO shouldn't get error for redeclaring "_"
|
||||
for (array) |_2| {
|
||||
for (array) |_| {
|
||||
counter += 1;
|
||||
continue :outer;
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\
|
||||
\\pub fn main() void {
|
||||
\\ privateFunction();
|
||||
\\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
|
||||
\\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream;
|
||||
\\ stdout.print("OK 2\n") catch unreachable;
|
||||
\\}
|
||||
\\
|
||||
@ -34,7 +34,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\// purposefully conflicting function with main.zig
|
||||
\\// but it's private so it should be OK
|
||||
\\fn privateFunction() void {
|
||||
\\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
|
||||
\\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream;
|
||||
\\ stdout.print("OK 1\n") catch unreachable;
|
||||
\\}
|
||||
\\
|
||||
@ -60,7 +60,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
tc.addSourceFile("foo.zig",
|
||||
\\use @import("std").io;
|
||||
\\pub fn foo_function() void {
|
||||
\\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
|
||||
\\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream;
|
||||
\\ stdout.print("OK\n") catch unreachable;
|
||||
\\}
|
||||
);
|
||||
@ -71,7 +71,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\
|
||||
\\pub fn bar_function() void {
|
||||
\\ if (foo_function()) {
|
||||
\\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
|
||||
\\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream;
|
||||
\\ stdout.print("OK\n") catch unreachable;
|
||||
\\ }
|
||||
\\}
|
||||
@ -103,7 +103,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\pub const a_text = "OK\n";
|
||||
\\
|
||||
\\pub fn ok() void {
|
||||
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
|
||||
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
|
||||
\\ stdout.print(b_text) catch unreachable;
|
||||
\\}
|
||||
);
|
||||
@ -121,7 +121,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\const io = @import("std").io;
|
||||
\\
|
||||
\\pub fn main() void {
|
||||
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
|
||||
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
|
||||
\\ stdout.print("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable;
|
||||
\\}
|
||||
, "Hello, world!\n0012 012 a\n");
|
||||
@ -274,7 +274,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\ var x_local : i32 = print_ok(x);
|
||||
\\}
|
||||
\\fn print_ok(val: @typeOf(x)) @typeOf(foo) {
|
||||
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
|
||||
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
|
||||
\\ stdout.print("OK\n") catch unreachable;
|
||||
\\ return 0;
|
||||
\\}
|
||||
@ -356,7 +356,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\pub fn main() void {
|
||||
\\ const bar = Bar {.field2 = 13,};
|
||||
\\ const foo = Foo {.field1 = bar,};
|
||||
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
|
||||
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
|
||||
\\ if (!foo.method()) {
|
||||
\\ stdout.print("BAD\n") catch unreachable;
|
||||
\\ }
|
||||
@ -370,7 +370,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
cases.add("defer with only fallthrough",
|
||||
\\const io = @import("std").io;
|
||||
\\pub fn main() void {
|
||||
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
|
||||
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
|
||||
\\ stdout.print("before\n") catch unreachable;
|
||||
\\ defer stdout.print("defer1\n") catch unreachable;
|
||||
\\ defer stdout.print("defer2\n") catch unreachable;
|
||||
@ -383,7 +383,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\const io = @import("std").io;
|
||||
\\const os = @import("std").os;
|
||||
\\pub fn main() void {
|
||||
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
|
||||
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
|
||||
\\ stdout.print("before\n") catch unreachable;
|
||||
\\ defer stdout.print("defer1\n") catch unreachable;
|
||||
\\ defer stdout.print("defer2\n") catch unreachable;
|
||||
@ -400,7 +400,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\ do_test() catch return;
|
||||
\\}
|
||||
\\fn do_test() !void {
|
||||
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
|
||||
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
|
||||
\\ stdout.print("before\n") catch unreachable;
|
||||
\\ defer stdout.print("defer1\n") catch unreachable;
|
||||
\\ errdefer stdout.print("deferErr\n") catch unreachable;
|
||||
@ -419,7 +419,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\ do_test() catch return;
|
||||
\\}
|
||||
\\fn do_test() !void {
|
||||
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
|
||||
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
|
||||
\\ stdout.print("before\n") catch unreachable;
|
||||
\\ defer stdout.print("defer1\n") catch unreachable;
|
||||
\\ errdefer stdout.print("deferErr\n") catch unreachable;
|
||||
@ -436,7 +436,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\const io = @import("std").io;
|
||||
\\
|
||||
\\pub fn main() void {
|
||||
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
|
||||
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
|
||||
\\ stdout.print(foo_txt) catch unreachable;
|
||||
\\}
|
||||
, "1234\nabcd\n");
|
||||
@ -456,7 +456,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\pub fn main() !void {
|
||||
\\ var args_it = os.args();
|
||||
\\ var stdout_file = try io.getStdOut();
|
||||
\\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
|
||||
\\ var stdout_adapter = io.FileOutStream.init(stdout_file);
|
||||
\\ const stdout = &stdout_adapter.stream;
|
||||
\\ var index: usize = 0;
|
||||
\\ _ = args_it.skip();
|
||||
@ -497,7 +497,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\pub fn main() !void {
|
||||
\\ var args_it = os.args();
|
||||
\\ var stdout_file = try io.getStdOut();
|
||||
\\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
|
||||
\\ var stdout_adapter = io.FileOutStream.init(stdout_file);
|
||||
\\ const stdout = &stdout_adapter.stream;
|
||||
\\ var index: usize = 0;
|
||||
\\ _ = args_it.skip();
|
||||
|
||||
@ -1,6 +1,49 @@
|
||||
const tests = @import("tests.zig");
|
||||
|
||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
cases.add(
|
||||
"switch with invalid expression parameter",
|
||||
\\export fn entry() void {
|
||||
\\ Test(i32);
|
||||
\\}
|
||||
\\fn Test(comptime T: type) void {
|
||||
\\ const x = switch (T) {
|
||||
\\ []u8 => |x| 123,
|
||||
\\ i32 => |x| 456,
|
||||
\\ else => unreachable,
|
||||
\\ };
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:7:17: error: switch on type 'type' provides no expression parameter",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
"function protoype with no body",
|
||||
\\fn foo() void;
|
||||
\\export fn entry() void {
|
||||
\\ foo();
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:1:1: error: non-extern function has no body",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
"@typeInfo causing depend on itself compile error",
|
||||
\\const start = struct {
|
||||
\\ fn crash() bug() {
|
||||
\\ return bug;
|
||||
\\ }
|
||||
\\};
|
||||
\\fn bug() void {
|
||||
\\ _ = @typeInfo(start).Struct;
|
||||
\\}
|
||||
\\export fn entry() void {
|
||||
\\ var boom = start.crash();
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:2:5: error: 'crash' depends on itself",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
"@handle() called outside of function definition",
|
||||
\\var handle_undef: promise = undefined;
|
||||
|
||||
@ -263,8 +263,8 @@ pub const CompareOutputContext = struct {
|
||||
var stdout = Buffer.initNull(b.allocator);
|
||||
var stderr = Buffer.initNull(b.allocator);
|
||||
|
||||
var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
|
||||
var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
|
||||
var stdout_file_in_stream = io.FileInStream.init(child.stdout.?);
|
||||
var stderr_file_in_stream = io.FileInStream.init(child.stderr.?);
|
||||
|
||||
stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable;
|
||||
stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable;
|
||||
@ -578,8 +578,8 @@ pub const CompileErrorContext = struct {
|
||||
var stdout_buf = Buffer.initNull(b.allocator);
|
||||
var stderr_buf = Buffer.initNull(b.allocator);
|
||||
|
||||
var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
|
||||
var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
|
||||
var stdout_file_in_stream = io.FileInStream.init(child.stdout.?);
|
||||
var stderr_file_in_stream = io.FileInStream.init(child.stderr.?);
|
||||
|
||||
stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
|
||||
stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;
|
||||
@ -842,8 +842,8 @@ pub const TranslateCContext = struct {
|
||||
var stdout_buf = Buffer.initNull(b.allocator);
|
||||
var stderr_buf = Buffer.initNull(b.allocator);
|
||||
|
||||
var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
|
||||
var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
|
||||
var stdout_file_in_stream = io.FileInStream.init(child.stdout.?);
|
||||
var stderr_file_in_stream = io.FileInStream.init(child.stderr.?);
|
||||
|
||||
stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
|
||||
stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user