From 245eed8afee899cb0d0a2dac60c8a1edc6be654d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 24 Apr 2017 12:01:19 -0400 Subject: [PATCH] better stack traces for ELF x86_64 --- src/analyze.cpp | 1 + src/codegen.cpp | 2 +- src/main.cpp | 2 +- std/debug.zig | 511 +++++++++++++++++++++++++++++++++----- std/dwarf.zig | 21 ++ std/elf.zig | 5 +- std/io.zig | 18 +- std/os/linux.zig | 74 +++++- std/os/path.zig | 5 +- std/special/bootstrap.zig | 2 + 10 files changed, 573 insertions(+), 68 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index d754535163..ebbb3e0689 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2894,6 +2894,7 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, ast_print(stderr, import_entry->root, 0); } + // TODO: assert that src_basename has no '/' in it import_entry->di_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname)); g->import_table.put(abs_full_path, import_entry); g->import_queue.append(import_entry); diff --git a/src/codegen.cpp b/src/codegen.cpp index 5e31abc62e..01b763d5b0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4647,7 +4647,7 @@ static void init(CodeGen *g, Buf *source_path) { bool is_optimized = g->is_release_build; const char *flags = ""; unsigned runtime_version = 0; - ZigLLVMDIFile *compile_unit_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(source_path), + ZigLLVMDIFile *compile_unit_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(g->root_out_name), buf_ptr(&g->root_package->root_src_dir)); g->compile_unit = ZigLLVMCreateCompileUnit(g->dbuilder, ZigLLVMLang_DW_LANG_C99(), compile_unit_file, buf_ptr(producer), is_optimized, flags, runtime_version, diff --git a/src/main.cpp b/src/main.cpp index 5713847e4c..8576bb6b42 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -475,7 +475,7 @@ int main(int argc, char **argv) { } } - bool need_name = (cmd == CmdBuild || cmd == CmdAsm || cmd == CmdLink); + bool need_name = (cmd == CmdBuild || cmd == CmdAsm || cmd == CmdLink || cmd == CmdParseH); Buf in_file_buf = BUF_INIT; diff --git a/std/debug.zig b/std/debug.zig index b3f6c21e9c..0d00931525 100644 --- a/std/debug.zig +++ b/std/debug.zig @@ -29,17 +29,27 @@ pub coldcc fn panic(comptime format: []const u8, args: ...) -> noreturn { } %%io.stderr.printf(format ++ "\n", args); - %%printStackTrace(); + %%writeStackTrace(&io.stderr, &global_allocator, io.stderr.isTty(), 1); + %%io.stderr.flush(); os.abort(); } pub fn printStackTrace() -> %void { - %return writeStackTrace(&io.stderr); + %return writeStackTrace(&io.stderr, &global_allocator, io.stderr.isTty(), 1); %return io.stderr.flush(); } -pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { +const GREEN = "\x1b[32;1m"; +const WHITE = "\x1b[37;1m"; +const DIM = "\x1b[2m"; +const RESET = "\x1b[0m"; + +pub var user_main_fn: ?fn() -> %void = null; + +pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool, + ignore_frame_count: usize) -> %void +{ switch (@compileVar("object_format")) { ObjectFormat.elf => { var stack_trace = ElfStackTrace { @@ -48,33 +58,67 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { .debug_info = undefined, .debug_abbrev = undefined, .debug_str = undefined, - .abbrev_table_list = List(AbbrevTableHeader).init(&global_allocator), - .compile_unit_list = List(CompileUnit).init(&global_allocator), + .debug_line = undefined, + .abbrev_table_list = List(AbbrevTableHeader).init(allocator), + .compile_unit_list = List(CompileUnit).init(allocator), }; const st = &stack_trace; st.self_exe_stream = %return io.openSelfExe(); defer st.self_exe_stream.close(); - %return st.elf.openStream(&global_allocator, &st.self_exe_stream); + %return st.elf.openStream(allocator, &st.self_exe_stream); defer st.elf.close(); st.debug_info = (%return st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; st.debug_abbrev = (%return st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; st.debug_str = (%return st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo; + st.debug_line = (%return st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo; %return scanAllCompileUnits(st); - %return out_stream.printf("(...work-in-progress stack unwinding code follows...)\n"); + var ignored_count: usize = 0; - var maybe_fp: ?&const u8 = @frameAddress(); - while (true) { - const fp = maybe_fp ?? break; - const return_address = *@intToPtr(&const usize, usize(fp) + @sizeOf(usize)); + var fp = usize(@frameAddress()); + while (fp != 0; fp = *@intToPtr(&const usize, fp)) { + if (ignored_count < ignore_frame_count) { + ignored_count += 1; + continue; + } + + const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize)); + + // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal + // at compile time. I'll call it issue #313 + const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; const compile_unit = findCompileUnit(st, return_address) ?? return error.MissingDebugInfo; - const name = %return compile_unit.die.getAttrString(st, DW.AT_name); - - %return out_stream.printf("{} -> {}\n", return_address, name); - maybe_fp = *@ptrCast(&const ?&const u8, fp); + const compile_unit_name = %return compile_unit.die.getAttrString(st, DW.AT_name); + try (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| { + defer line_info.deinit(); + %return out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ + DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", + line_info.file_name, line_info.line, line_info.column, + return_address, compile_unit_name); + try (printLineFromFile(st.allocator(), out_stream, line_info)) { + if (line_info.column == 0) { + %return out_stream.write("\n"); + } else { + {var col_i: usize = 1; while (col_i < line_info.column; col_i += 1) { + %return out_stream.writeByte(' '); + }} + %return out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); + } + } else |err| switch (err) { + error.EndOfFile, error.PathNotFound => {}, + else => return err, + } + } else |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => { + %return out_stream.print(ptr_hex ++ " in ??? ({})\n", + return_address, compile_unit_name); + }, + else => return err, + }; + %return out_stream.flush(); } }, ObjectFormat.coff => { @@ -89,14 +133,56 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { } } +fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_info: &const LineInfo) -> %void { + var f = %return io.InStream.open(line_info.file_name, allocator); + defer f.close(); + // TODO fstat and make sure that the file has the correct size + + var buf: [os.page_size]u8 = undefined; + var line: usize = 1; + var column: usize = 1; + var abs_index: usize = 0; + while (true) { + const amt_read = %return f.read(buf[0...]); + const slice = buf[0...amt_read]; + + for (slice) |byte| { + if (line == line_info.line) { + %return out_stream.writeByte(byte); + if (byte == '\n') { + return; + } + } + if (byte == '\n') { + line += 1; + column = 1; + } else { + column += 1; + } + } + + if (amt_read < buf.len) + return error.EndOfFile; + } +} + const ElfStackTrace = struct { self_exe_stream: io.InStream, elf: elf.Elf, debug_info: &elf.SectionHeader, debug_abbrev: &elf.SectionHeader, debug_str: &elf.SectionHeader, + debug_line: &elf.SectionHeader, abbrev_table_list: List(AbbrevTableHeader), compile_unit_list: List(CompileUnit), + + pub fn allocator(self: &const ElfStackTrace) -> &mem.Allocator { + return self.abbrev_table_list.allocator; + } + + pub fn readString(self: &ElfStackTrace) -> %[]u8 { + return readStringRaw(self.allocator(), &self.self_exe_stream); + } }; const CompileUnit = struct { @@ -104,6 +190,7 @@ const CompileUnit = struct { die: &Die, pc_start: u64, pc_end: u64, + index: usize, }; const AbbrevTable = List(AbbrevTableEntry); @@ -197,44 +284,142 @@ const Die = struct { } }; -fn readString(in_stream: &io.InStream) -> %[]u8 { - var buf = List(u8).init(&global_allocator); +const FileEntry = struct { + file_name: []const u8, + dir_index: usize, + mtime: usize, + len_bytes: usize, +}; + +const LineInfo = struct { + line: usize, + column: usize, + file_name: []u8, + allocator: &mem.Allocator, + + fn deinit(self: &const LineInfo) { + self.allocator.free(self.file_name); + } +}; + +const LineNumberProgram = struct { + address: usize, + file: usize, + line: isize, + column: usize, + is_stmt: bool, + basic_block: bool, + end_sequence: bool, + + target_address: usize, + include_dirs: []const []const u8, + file_entries: &List(FileEntry), + + prev_address: usize, + prev_file: usize, + prev_line: isize, + prev_column: usize, + prev_is_stmt: bool, + prev_basic_block: bool, + prev_end_sequence: bool, + + pub fn init(is_stmt: bool, include_dirs: []const []const u8, + file_entries: &List(FileEntry), target_address: usize) -> LineNumberProgram + { + LineNumberProgram { + .address = 0, + .file = 1, + .line = 1, + .column = 0, + .is_stmt = is_stmt, + .basic_block = false, + .end_sequence = false, + .include_dirs = include_dirs, + .file_entries = file_entries, + .target_address = target_address, + .prev_address = 0, + .prev_file = undefined, + .prev_line = undefined, + .prev_column = undefined, + .prev_is_stmt = undefined, + .prev_basic_block = undefined, + .prev_end_sequence = undefined, + } + } + + pub fn checkLineMatch(self: &LineNumberProgram) -> %?LineInfo { + if (self.target_address >= self.prev_address and self.target_address < self.address) { + const file_entry = if (self.prev_file == 0) { + return error.MissingDebugInfo; + } else if (self.prev_file - 1 >= self.file_entries.len) { + return error.InvalidDebugInfo; + } else { + &self.file_entries.items[self.prev_file - 1] + }; + const dir_name = if (file_entry.dir_index >= self.include_dirs.len) { + return error.InvalidDebugInfo; + } else { + self.include_dirs[file_entry.dir_index] + }; + const file_name = %return os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name); + %defer self.file_entries.allocator.free(file_name); + return LineInfo { + .line = if (self.prev_line >= 0) usize(self.prev_line) else 0, + .column = self.prev_column, + .file_name = file_name, + .allocator = self.file_entries.allocator, + }; + } + + self.prev_address = self.address; + self.prev_file = self.file; + self.prev_line = self.line; + self.prev_column = self.column; + self.prev_is_stmt = self.is_stmt; + self.prev_basic_block = self.basic_block; + self.prev_end_sequence = self.end_sequence; + return null; + } +}; + +fn readStringRaw(allocator: &mem.Allocator, in_stream: &io.InStream) -> %[]u8 { + var buf = List(u8).init(allocator); while (true) { const byte = %return in_stream.readByte(); if (byte == 0) break; %return buf.append(byte); } - return buf.items; + return buf.toSlice(); } fn getString(st: &ElfStackTrace, offset: u64) -> %[]u8 { const pos = st.debug_str.offset + offset; %return st.self_exe_stream.seekTo(pos); - return readString(&st.self_exe_stream); + return st.readString(); } -fn readAllocBytes(in_stream: &io.InStream, size: usize) -> %[]u8 { +fn readAllocBytes(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %[]u8 { const buf = %return global_allocator.alloc(u8, size); %defer global_allocator.free(buf); - if ((%return in_stream.read(buf)) < size) return error.Eof; + if ((%return in_stream.read(buf)) < size) return error.EndOfFile; return buf; } -fn parseFormValueBlockLen(in_stream: &io.InStream, size: usize) -> %FormValue { - const buf = %return readAllocBytes(in_stream, size); +fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue { + const buf = %return readAllocBytes(allocator, in_stream, size); return FormValue.Block { buf }; } -fn parseFormValueBlock(in_stream: &io.InStream, size: usize) -> %FormValue { +fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue { const block_len = %return in_stream.readVarInt(false, usize, size); - return parseFormValueBlockLen(in_stream, block_len); + return parseFormValueBlockLen(allocator, in_stream, block_len); } -fn parseFormValueConstant(in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue { +fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue { FormValue.Const { Constant { .signed = signed, - .payload = %return readAllocBytes(in_stream, size), + .payload = %return readAllocBytes(allocator, in_stream, size), }} } @@ -256,38 +441,38 @@ fn parseFormValueTargetAddrSize(in_stream: &io.InStream) -> %u64 { }; } -fn parseFormValueRefLen(in_stream: &io.InStream, size: usize) -> %FormValue { - const buf = %return readAllocBytes(in_stream, size); +fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue { + const buf = %return readAllocBytes(allocator, in_stream, size); return FormValue.Ref { buf }; } -fn parseFormValueRef(in_stream: &io.InStream, comptime T: type) -> %FormValue { +fn parseFormValueRef(allocator: &mem.Allocator, in_stream: &io.InStream, comptime T: type) -> %FormValue { const block_len = %return in_stream.readIntLe(T); - return parseFormValueRefLen(in_stream, block_len); + return parseFormValueRefLen(allocator, in_stream, block_len); } -fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormValue { +fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormValue { return switch (form_id) { DW.FORM_addr => FormValue.Address { %return parseFormValueTargetAddrSize(in_stream) }, - DW.FORM_block1 => parseFormValueBlock(in_stream, 1), - DW.FORM_block2 => parseFormValueBlock(in_stream, 2), - DW.FORM_block4 => parseFormValueBlock(in_stream, 4), + DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), + DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2), + DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4), DW.FORM_block => { const block_len = %return readULeb128(in_stream); - parseFormValueBlockLen(in_stream, block_len) + parseFormValueBlockLen(allocator, in_stream, block_len) }, - DW.FORM_data1 => parseFormValueConstant(in_stream, false, 1), - DW.FORM_data2 => parseFormValueConstant(in_stream, false, 2), - DW.FORM_data4 => parseFormValueConstant(in_stream, false, 4), - DW.FORM_data8 => parseFormValueConstant(in_stream, false, 8), + DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1), + DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2), + DW.FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4), + DW.FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8), DW.FORM_udata, DW.FORM_sdata => { const block_len = %return readULeb128(in_stream); const signed = form_id == DW.FORM_sdata; - parseFormValueConstant(in_stream, signed, block_len) + parseFormValueConstant(allocator, in_stream, signed, block_len) }, DW.FORM_exprloc => { const size = %return readULeb128(in_stream); - const buf = %return readAllocBytes(in_stream, size); + const buf = %return readAllocBytes(allocator, in_stream, size); return FormValue.ExprLoc { buf }; }, DW.FORM_flag => FormValue.Flag { (%return in_stream.readByte()) != 0 }, @@ -296,30 +481,31 @@ fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormVa %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, - DW.FORM_ref1 => parseFormValueRef(in_stream, u8), - DW.FORM_ref2 => parseFormValueRef(in_stream, u16), - DW.FORM_ref4 => parseFormValueRef(in_stream, u32), - DW.FORM_ref8 => parseFormValueRef(in_stream, u64), + DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, u8), + DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, u16), + DW.FORM_ref4 => parseFormValueRef(allocator, in_stream, u32), + DW.FORM_ref8 => parseFormValueRef(allocator, in_stream, u64), DW.FORM_ref_udata => { const ref_len = %return readULeb128(in_stream); - parseFormValueRefLen(in_stream, ref_len) + parseFormValueRefLen(allocator, in_stream, ref_len) }, DW.FORM_ref_addr => FormValue.RefAddr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, DW.FORM_ref_sig8 => FormValue.RefSig8 { %return in_stream.readIntLe(u64) }, - DW.FORM_string => FormValue.String { %return readString(in_stream) }, + DW.FORM_string => FormValue.String { %return readStringRaw(allocator, in_stream) }, DW.FORM_strp => FormValue.StrPtr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, DW.FORM_indirect => { const child_form_id = %return readULeb128(in_stream); - parseFormValue(in_stream, child_form_id, is_64) + parseFormValue(allocator, in_stream, child_form_id, is_64) }, else => error.InvalidDebugInfo, } } -fn parseAbbrevTable(in_stream: &io.InStream) -> %AbbrevTable { - var result = AbbrevTable.init(&global_allocator); +fn parseAbbrevTable(st: &ElfStackTrace) -> %AbbrevTable { + const in_stream = &st.self_exe_stream; + var result = AbbrevTable.init(st.allocator()); while (true) { const abbrev_code = %return readULeb128(in_stream); if (abbrev_code == 0) @@ -328,7 +514,7 @@ fn parseAbbrevTable(in_stream: &io.InStream) -> %AbbrevTable { .abbrev_code = abbrev_code, .tag_id = %return readULeb128(in_stream), .has_children = (%return in_stream.readByte()) == DW.CHILDREN_yes, - .attrs = List(AbbrevAttr).init(&global_allocator), + .attrs = List(AbbrevAttr).init(st.allocator()), }); const attrs = &result.items[result.len - 1].attrs; @@ -356,7 +542,7 @@ fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) -> %&const AbbrevTable %return st.self_exe_stream.seekTo(st.debug_abbrev.offset + abbrev_offset); %return st.abbrev_table_list.append(AbbrevTableHeader { .offset = abbrev_offset, - .table = %return parseAbbrevTable(&st.self_exe_stream), + .table = %return parseAbbrevTable(st), }); return &st.abbrev_table_list.items[st.abbrev_table_list.len - 1].table; } @@ -369,28 +555,235 @@ fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) -> ?& return null; } -fn parseDie(in_stream: &io.InStream, abbrev_table: &const AbbrevTable, is_64: bool) -> %Die { +fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) -> %Die { + const in_stream = &st.self_exe_stream; const abbrev_code = %return readULeb128(in_stream); const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo; var result = Die { .tag_id = table_entry.tag_id, .has_children = table_entry.has_children, - .attrs = List(Die.Attr).init(&global_allocator), + .attrs = List(Die.Attr).init(st.allocator()), }; %return result.attrs.resize(table_entry.attrs.len); for (table_entry.attrs.toSliceConst()) |attr, i| { result.attrs.items[i] = Die.Attr { .id = attr.attr_id, - .value = %return parseFormValue(in_stream, attr.form_id, is_64), + .value = %return parseFormValue(st.allocator(), &st.self_exe_stream, attr.form_id, is_64), }; } return result; } +fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, target_address: usize) -> %LineInfo { + const compile_unit_cwd = %return compile_unit.die.getAttrString(st, DW.AT_comp_dir); + + const in_stream = &st.self_exe_stream; + const debug_line_end = st.debug_line.offset + st.debug_line.size; + var this_offset = st.debug_line.offset; + var this_index: usize = 0; + + while (this_offset < debug_line_end; this_index += 1) { + %return in_stream.seekTo(this_offset); + + var is_64: bool = undefined; + const unit_length = %return readInitialLength(in_stream, &is_64); + if (unit_length == 0) + return error.MissingDebugInfo; + const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); + + if (compile_unit.index != this_index) { + this_offset += next_offset; + continue; + } + + const version = %return in_stream.readInt(st.elf.is_big_endian, u16); + if (version != 2) return error.InvalidDebugInfo; + + const prologue_length = %return in_stream.readInt(st.elf.is_big_endian, u32); + const prog_start_offset = (%return in_stream.getPos()) + prologue_length; + + const minimum_instruction_length = %return in_stream.readByte(); + if (minimum_instruction_length == 0) return error.InvalidDebugInfo; + + const default_is_stmt = (%return in_stream.readByte()) != 0; + const line_base = %return in_stream.readByteSigned(); + + const line_range = %return in_stream.readByte(); + if (line_range == 0) + return error.InvalidDebugInfo; + + const opcode_base = %return in_stream.readByte(); + + const standard_opcode_lengths = %return st.allocator().alloc(u8, opcode_base - 1); + + {var i: usize = 0; while (i < opcode_base - 1; i += 1) { + standard_opcode_lengths[i] = %return in_stream.readByte(); + }} + + var include_directories = List([]u8).init(st.allocator()); + %return include_directories.append(compile_unit_cwd); + while (true) { + const dir = %return st.readString(); + if (dir.len == 0) + break; + %return include_directories.append(dir); + } + + var file_entries = List(FileEntry).init(st.allocator()); + var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), + &file_entries, target_address); + + while (true) { + const file_name = %return st.readString(); + if (file_name.len == 0) + break; + const dir_index = %return readULeb128(in_stream); + const mtime = %return readULeb128(in_stream); + const len_bytes = %return readULeb128(in_stream); + %return file_entries.append(FileEntry { + .file_name = file_name, + .dir_index = dir_index, + .mtime = mtime, + .len_bytes = len_bytes, + }); + } + + %return in_stream.seekTo(prog_start_offset); + + while (true) { + //const pos = (%return in_stream.getPos()) - this_offset; + //if (pos == 0x1a3) @breakpoint(); + //%%io.stderr.printf("\n{x8}\n", pos); + + const opcode = %return in_stream.readByte(); + + var sub_op: u8 = undefined; // TODO move this to the correct scope and fix the compiler crash + if (opcode == DW.LNS_extended_op) { + const op_size = %return readULeb128(in_stream); + if (op_size < 1) + return error.InvalidDebugInfo; + sub_op = %return in_stream.readByte(); + switch (sub_op) { + DW.LNE_end_sequence => { + //%%io.stdout.printf(" [0x{x8}] End Sequence\n", pos); + prog.end_sequence = true; + test (%return prog.checkLineMatch()) |info| return info; + return error.MissingDebugInfo; + }, + DW.LNE_set_address => { + const addr = %return in_stream.readInt(st.elf.is_big_endian, usize); + prog.address = addr; + + //%%io.stdout.printf(" [0x{x8}] Extended opcode {}: set Address to 0x{x}\n", + // pos, sub_op, addr); + }, + DW.LNE_define_file => { + //%%io.stdout.printf(" [0x{x8}] Define File\n", pos); + + const file_name = %return st.readString(); + const dir_index = %return readULeb128(in_stream); + const mtime = %return readULeb128(in_stream); + const len_bytes = %return readULeb128(in_stream); + %return file_entries.append(FileEntry { + .file_name = file_name, + .dir_index = dir_index, + .mtime = mtime, + .len_bytes = len_bytes, + }); + }, + else => { + %return in_stream.seekForward(op_size - 1); + }, + } + } else if (opcode >= opcode_base) { + // special opcodes + const adjusted_opcode = opcode - opcode_base; + const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); + const inc_line = i32(line_base) + i32(adjusted_opcode % line_range); + prog.line += inc_line; + prog.address += inc_addr; + //%%io.stdout.printf( + // " [0x{x8}] Special opcode {}: advance Address by {} to 0x{x} and Line by {} to {}\n", + // pos, adjusted_opcode, inc_addr, prog.address, inc_line, prog.line); + test (%return prog.checkLineMatch()) |info| return info; + prog.basic_block = false; + } else { + switch (opcode) { + DW.LNS_copy => { + //%%io.stdout.printf(" [0x{x8}] Copy\n", pos); + + test (%return prog.checkLineMatch()) |info| return info; + prog.basic_block = false; + }, + DW.LNS_advance_pc => { + const arg = %return readULeb128(in_stream); + prog.address += arg * minimum_instruction_length; + + //%%io.stdout.printf(" [0x{x8}] Advance PC by {} to 0x{x}\n", pos, arg, prog.address); + }, + DW.LNS_advance_line => { + const arg = %return readILeb128(in_stream); + prog.line += arg; + + //%%io.stdout.printf(" [0x{x8}] Advance Line by {} to {}\n", pos, arg, prog.line); + }, + DW.LNS_set_file => { + const arg = %return readULeb128(in_stream); + prog.file = arg; + + //%%io.stdout.printf(" [0x{x8}] Set File Name to entry {} in the File Name Table\n", + // pos, arg); + }, + DW.LNS_set_column => { + const arg = %return readULeb128(in_stream); + prog.column = arg; + + //%%io.stdout.printf(" [0x{x8}] Set column to {}\n", pos, arg); + }, + DW.LNS_negate_stmt => { + prog.is_stmt = !prog.is_stmt; + + //%%io.stdout.printf(" [0x{x8}] Set is_stmt to {}\n", pos, if (prog.is_stmt) u8(1) else u8(0)); + }, + DW.LNS_set_basic_block => { + prog.basic_block = true; + }, + DW.LNS_const_add_pc => { + const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); + prog.address += inc_addr; + + //%%io.stdout.printf(" [0x{x8}] Advance PC by constant {} to 0x{x}\n", + // pos, inc_addr, prog.address); + }, + DW.LNS_fixed_advance_pc => { + const arg = %return in_stream.readInt(st.elf.is_big_endian, u16); + prog.address += arg; + }, + DW.LNS_set_prologue_end => { + //%%io.stdout.printf(" [0x{x8}] Set prologue_end to true\n", pos); + }, + else => { + if (opcode - 1 >= standard_opcode_lengths.len) + return error.InvalidDebugInfo; + //%%io.stdout.printf(" [0x{x8}] unknown op code {}\n", pos, opcode); + const len_bytes = standard_opcode_lengths[opcode - 1]; + %return in_stream.seekForward(len_bytes); + }, + } + } + } + + this_offset += next_offset; + } + + return error.MissingDebugInfo; +} + fn scanAllCompileUnits(st: &ElfStackTrace) -> %void { const debug_info_end = st.debug_info.offset + st.debug_info.size; var this_unit_offset = st.debug_info.offset; + var cu_index: usize = 0; while (this_unit_offset < debug_info_end) { %return st.self_exe_stream.seekTo(this_unit_offset); @@ -417,8 +810,8 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void { %return st.self_exe_stream.seekTo(compile_unit_pos); - const compile_unit_die = (%return global_allocator.alloc(Die, 1)).ptr; - *compile_unit_die = %return parseDie(&st.self_exe_stream, abbrev_table, is_64); + const compile_unit_die = %return st.allocator().create(Die); + *compile_unit_die = %return parseDie(st, abbrev_table, is_64); if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; @@ -439,9 +832,11 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void { .pc_start = low_pc, .pc_end = pc_end, .die = compile_unit_die, + .index = cu_index, }); this_unit_offset += next_offset; + cu_index += 1; } } diff --git a/std/dwarf.zig b/std/dwarf.zig index ba554a198f..04456d9e5c 100644 --- a/std/dwarf.zig +++ b/std/dwarf.zig @@ -620,3 +620,24 @@ pub const CFA_GNU_negative_offset_extended = 0x2f; pub const CHILDREN_no = 0x00; pub const CHILDREN_yes = 0x01; + +pub const LNS_extended_op = 0x00; +pub const LNS_copy = 0x01; +pub const LNS_advance_pc = 0x02; +pub const LNS_advance_line = 0x03; +pub const LNS_set_file = 0x04; +pub const LNS_set_column = 0x05; +pub const LNS_negate_stmt = 0x06; +pub const LNS_set_basic_block = 0x07; +pub const LNS_const_add_pc = 0x08; +pub const LNS_fixed_advance_pc = 0x09; +pub const LNS_set_prologue_end = 0x0a; +pub const LNS_set_epilogue_begin = 0x0b; +pub const LNS_set_isa = 0x0c; + +pub const LNE_end_sequence = 0x01; +pub const LNE_set_address = 0x02; +pub const LNE_define_file = 0x03; +pub const LNE_set_discriminator = 0x04; +pub const LNE_lo_user = 0x80; +pub const LNE_hi_user = 0xff; diff --git a/std/elf.zig b/std/elf.zig index cecce42075..ac02425399 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -250,14 +250,13 @@ pub const Elf = struct { { const null_byte = %return elf.in_stream.readByte(); - if (null_byte == 0) return (?&SectionHeader)(section); + if (null_byte == 0) return section; } next_section: } - const null_sh: ?&SectionHeader = null; - return null_sh; + return null; } pub fn seekToSection(elf: &Elf, section: &SectionHeader) -> %void { diff --git a/std/io.zig b/std/io.zig index 2b48e1d899..0bf34acaa4 100644 --- a/std/io.zig +++ b/std/io.zig @@ -55,7 +55,7 @@ error NoDevice; error PathNotFound; error NoMem; error Unseekable; -error Eof; +error EndOfFile; pub const OpenRead = 0b0001; pub const OpenWrite = 0b0010; @@ -153,6 +153,10 @@ pub const OutStream = struct { assert(self.index == 0); os.posixClose(self.fd); } + + pub fn isTty(self: &const OutStream) -> bool { + return os.posix.isatty(self.fd); + } }; // TODO created a BufferedInStream struct and move some of this code there @@ -219,7 +223,7 @@ pub const InStream = struct { pub fn readNoEof(is: &InStream, buf: []u8) -> %void { const amt_read = %return is.read(buf); - if (amt_read < buf.len) return error.Eof; + if (amt_read < buf.len) return error.EndOfFile; } pub fn readByte(is: &InStream) -> %u8 { @@ -228,6 +232,12 @@ pub const InStream = struct { return result[0]; } + pub fn readByteSigned(is: &InStream) -> %i8 { + var result: [1]i8 = undefined; + %return is.readNoEof(([]u8)(result[0...])); + return result[0]; + } + pub fn readIntLe(is: &InStream, comptime T: type) -> %T { is.readInt(false, T) } @@ -342,6 +352,10 @@ pub const InStream = struct { %return buf.resize(actual_buf_len + os.page_size); } } + + pub fn isTty(self: &const InStream) -> bool { + return os.posix.isatty(self.fd); + } }; pub fn openSelfExe() -> %InStream { diff --git a/std/os/linux.zig b/std/os/linux.zig index 86a37e1b1c..6db552e640 100644 --- a/std/os/linux.zig +++ b/std/os/linux.zig @@ -251,6 +251,63 @@ pub const DT_LNK = 10; pub const DT_SOCK = 12; pub const DT_WHT = 14; + +pub const TCGETS = 0x5401; +pub const TCSETS = 0x5402; +pub const TCSETSW = 0x5403; +pub const TCSETSF = 0x5404; +pub const TCGETA = 0x5405; +pub const TCSETA = 0x5406; +pub const TCSETAW = 0x5407; +pub const TCSETAF = 0x5408; +pub const TCSBRK = 0x5409; +pub const TCXONC = 0x540A; +pub const TCFLSH = 0x540B; +pub const TIOCEXCL = 0x540C; +pub const TIOCNXCL = 0x540D; +pub const TIOCSCTTY = 0x540E; +pub const TIOCGPGRP = 0x540F; +pub const TIOCSPGRP = 0x5410; +pub const TIOCOUTQ = 0x5411; +pub const TIOCSTI = 0x5412; +pub const TIOCGWINSZ = 0x5413; +pub const TIOCSWINSZ = 0x5414; +pub const TIOCMGET = 0x5415; +pub const TIOCMBIS = 0x5416; +pub const TIOCMBIC = 0x5417; +pub const TIOCMSET = 0x5418; +pub const TIOCGSOFTCAR = 0x5419; +pub const TIOCSSOFTCAR = 0x541A; +pub const FIONREAD = 0x541B; +pub const TIOCINQ = FIONREAD; +pub const TIOCLINUX = 0x541C; +pub const TIOCCONS = 0x541D; +pub const TIOCGSERIAL = 0x541E; +pub const TIOCSSERIAL = 0x541F; +pub const TIOCPKT = 0x5420; +pub const FIONBIO = 0x5421; +pub const TIOCNOTTY = 0x5422; +pub const TIOCSETD = 0x5423; +pub const TIOCGETD = 0x5424; +pub const TCSBRKP = 0x5425; +pub const TIOCSBRK = 0x5427; +pub const TIOCCBRK = 0x5428; +pub const TIOCGSID = 0x5429; +pub const TIOCGRS485 = 0x542E; +pub const TIOCSRS485 = 0x542F; +pub const TIOCGPTN = 0x80045430; +pub const TIOCSPTLCK = 0x40045431; +pub const TIOCGDEV = 0x80045432; +pub const TCGETX = 0x5432; +pub const TCSETX = 0x5433; +pub const TCSETXF = 0x5434; +pub const TCSETXW = 0x5435; +pub const TIOCSIG = 0x40045436; +pub const TIOCVHANGUP = 0x5437; +pub const TIOCGPKT = 0x80045438; +pub const TIOCGPTLCK = 0x80045439; +pub const TIOCGEXCL = 0x80045440; + fn unsigned(s: i32) -> u32 { *@ptrCast(&u32, &s) } fn signed(s: u32) -> i32 { *@ptrCast(&i32, &s) } pub fn WEXITSTATUS(s: i32) -> i32 { signed((unsigned(s) & 0xff00) >> 8) } @@ -260,6 +317,14 @@ pub fn WIFEXITED(s: i32) -> bool { WTERMSIG(s) == 0 } pub fn WIFSTOPPED(s: i32) -> bool { (u16)(((unsigned(s)&0xffff)*%0x10001)>>8) > 0x7f00 } pub fn WIFSIGNALED(s: i32) -> bool { (unsigned(s)&0xffff)-%1 < 0xff } + +pub const winsize = extern struct { + ws_row: u16, + ws_col: u16, + ws_xpixel: u16, + ws_ypixel: u16, +}; + /// Get the errno from a syscall return value, or 0 for no error. pub fn getErrno(r: usize) -> usize { const signed_r = *@ptrCast(&const isize, &r); @@ -286,6 +351,11 @@ pub fn getdents(fd: i32, dirp: &u8, count: usize) -> usize { arch.syscall3(arch.SYS_getdents, usize(fd), usize(dirp), usize(count)) } +pub fn isatty(fd: i32) -> bool { + var wsz: winsize = undefined; + return arch.syscall3(arch.SYS_ioctl, usize(fd), TIOCGWINSZ, usize(&wsz)) == 0; +} + pub fn mkdir(path: &const u8, mode: usize) -> usize { arch.syscall2(arch.SYS_mkdir, usize(path), mode) } @@ -440,7 +510,7 @@ pub const iovec = extern struct { // //export struct ifreq { // ifrn_name: [IF_NAMESIZE]u8, -// union { +// union { // ifru_addr: sockaddr, // ifru_dstaddr: sockaddr, // ifru_broadaddr: sockaddr, @@ -453,7 +523,7 @@ pub const iovec = extern struct { // ifru_slave: [IF_NAMESIZE]u8, // ifru_newname: [IF_NAMESIZE]u8, // ifru_data: &u8, -// } ifr_ifru; +// } ifr_ifru; //} // diff --git a/std/os/path.zig b/std/os/path.zig index 1f82153a89..32188b75bc 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -30,7 +30,7 @@ pub fn join(allocator: &Allocator, paths: ...) -> %[]u8 { mem.copy(u8, buf[buf_index...], arg); buf_index += arg.len; if (path_i >= paths.len) break; - if (arg[arg.len - 1] != sep) { + if (buf[buf_index - 1] != sep) { buf[buf_index] = sep; buf_index += 1; } @@ -45,6 +45,9 @@ test "os.path.join" { assert(mem.eql(u8, %%join(&debug.global_allocator, "/", "a", "b/", "c"), "/a/b/c")); assert(mem.eql(u8, %%join(&debug.global_allocator, "/a/", "b/", "c"), "/a/b/c")); + + assert(mem.eql(u8, %%join(&debug.global_allocator, "/home/andy/dev/zig/build/lib/zig/std", "io.zig"), + "/home/andy/dev/zig/build/lib/zig/std/io.zig")); } pub fn isAbsolute(path: []const u8) -> bool { diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 3fa9bcaf0d..f7e6a35c88 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -44,6 +44,8 @@ fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void { while (envp[env_count] != null; env_count += 1) {} std.os.environ_raw = @ptrCast(&&u8, envp)[0...env_count]; + std.debug.user_main_fn = root.main; + return root.main(); }