From 3620b67c264c853442f0c1156f72ed699efe5fea Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 8 Feb 2020 14:15:28 +0100 Subject: [PATCH 01/99] debug: Split the DWARF stuff in its own file --- lib/std/debug.zig | 997 ++------------------------- lib/std/dwarf.zig | 1495 +++++++++++++++++++++++----------------- lib/std/dwarf_bits.zig | 682 ++++++++++++++++++ 3 files changed, 1586 insertions(+), 1588 deletions(-) create mode 100644 lib/std/dwarf_bits.zig diff --git a/lib/std/debug.zig b/lib/std/debug.zig index efe4f1fa76..65b591199b 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -38,6 +38,18 @@ const Module = struct { checksum_offset: ?usize, }; +pub const LineInfo = struct { + line: u64, + column: u64, + file_name: []const u8, + allocator: ?*mem.Allocator, + + fn deinit(self: LineInfo) void { + const allocator = self.allocator orelse return; + allocator.free(self.file_name); + } +}; + /// Tries to write to stderr, unbuffered, and ignores any error returned. /// Does not append a newline. var stderr_file: File = undefined; @@ -714,7 +726,35 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt } pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { - return debug_info.printSourceAtAddress(out_stream, address, tty_config, printLineFromFileAnyOs); + const compile_unit = debug_info.findCompileUnit(address) catch { + return printLineInfo( + out_stream, + null, + address, + "???", + "???", + tty_config, + printLineFromFileAnyOs, + ); + }; + + const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); + const symbol_name = debug_info.getSymbolName(address) orelse "???"; + const line_info = debug_info.getLineNumberInfo(compile_unit.*, address) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => null, + else => return err, + }; + defer if (line_info) |li| li.deinit(); + + try printLineInfo( + out_stream, + line_info, + address, + symbol_name, + compile_unit_name, + tty_config, + printLineFromFileAnyOs, + ); } fn printLineInfo( @@ -958,36 +998,24 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { return list.toOwnedSlice(); } -fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Section { +fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DW.DwarfInfo.Section { const elf_header = (try elf_file.findSection(name)) orelse return null; - return DwarfInfo.Section{ + return DW.DwarfInfo.Section{ .offset = elf_header.sh_offset, .size = elf_header.sh_size, }; } -/// Initialize DWARF info. The caller has the responsibility to initialize most -/// the DwarfInfo fields before calling. These fields can be left undefined: -/// * abbrev_table_list -/// * compile_unit_list -pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { - di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); - di.compile_unit_list = ArrayList(CompileUnit).init(allocator); - di.func_list = ArrayList(Func).init(allocator); - try di.scanAllFunctions(); - try di.scanAllCompileUnits(); -} - /// TODO resources https://github.com/ziglang/zig/issues/4353 pub fn openElfDebugInfo( allocator: *mem.Allocator, data: []u8, -) !DwarfInfo { +) !DW.DwarfInfo { var seekable_stream = io.SliceSeekableInStream.init(data); var efile = try elf.Elf.openStream( allocator, - @ptrCast(*DwarfSeekableStream, &seekable_stream.seekable_stream), - @ptrCast(*DwarfInStream, &seekable_stream.stream), + @ptrCast(*DW.DwarfSeekableStream, &seekable_stream.seekable_stream), + @ptrCast(*DW.DwarfInStream, &seekable_stream.stream), ); defer efile.close(); @@ -1001,7 +1029,7 @@ pub fn openElfDebugInfo( return error.MissingDebugInfo; const opt_debug_ranges = try efile.findSection(".debug_ranges"); - var di = DwarfInfo{ + var di = DW.DwarfInfo{ .endian = efile.endian, .debug_info = (data[@intCast(usize, debug_info.sh_offset)..@intCast(usize, debug_info.sh_offset + debug_info.sh_size)]), .debug_abbrev = (data[@intCast(usize, debug_abbrev.sh_offset)..@intCast(usize, debug_abbrev.sh_offset + debug_abbrev.sh_size)]), @@ -1013,12 +1041,12 @@ pub fn openElfDebugInfo( null, }; - try openDwarfDebugInfo(&di, allocator); + try DW.openDwarfDebugInfo(&di, allocator); return di; } /// TODO resources https://github.com/ziglang/zig/issues/4353 -fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DwarfInfo { +fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DW.DwarfInfo { var exe_file = try fs.openSelfExe(); errdefer exe_file.close(); @@ -1162,532 +1190,6 @@ const MachoSymbol = struct { } }; -pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror); -pub const DwarfInStream = io.InStream(anyerror); - -pub const DwarfInfo = struct { - endian: builtin.Endian, - // No memory is owned by the DwarfInfo - debug_info: []u8, - debug_abbrev: []u8, - debug_str: []u8, - debug_line: []u8, - debug_ranges: ?[]u8, - // Filled later by the initializer - abbrev_table_list: ArrayList(AbbrevTableHeader) = undefined, - compile_unit_list: ArrayList(CompileUnit) = undefined, - func_list: ArrayList(Func) = undefined, - - pub fn allocator(self: DwarfInfo) *mem.Allocator { - return self.abbrev_table_list.allocator; - } - - /// This function works in freestanding mode. - /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void - pub fn printSourceAtAddress( - self: *DwarfInfo, - out_stream: var, - address: usize, - tty_config: TTY.Config, - comptime printLineFromFile: var, - ) !void { - const compile_unit = self.findCompileUnit(address) catch { - return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFile); - }; - - const compile_unit_name = try compile_unit.die.getAttrString(self, DW.AT_name); - const symbol_name = self.getSymbolName(address) orelse "???"; - const line_info = self.getLineNumberInfo(compile_unit.*, address) catch |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => null, - else => return err, - }; - defer if (line_info) |li| li.deinit(); - - try printLineInfo( - out_stream, - line_info, - address, - symbol_name, - compile_unit_name, - tty_config, - printLineFromFile, - ); - } - - fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 { - for (di.func_list.toSliceConst()) |*func| { - if (func.pc_range) |range| { - if (address >= range.start and address < range.end) { - return func.name; - } - } - } - - return null; - } - - fn scanAllFunctions(di: *DwarfInfo) !void { - var s = io.SliceSeekableInStream.init(di.debug_info); - var this_unit_offset: u64 = 0; - - while (true) { - s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) { - error.EndOfStream => return, - else => return err, - }; - - var is_64: bool = undefined; - const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64); - if (unit_length == 0) return; - const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); - - const version = try s.stream.readInt(u16, di.endian); - if (version < 2 or version > 5) return error.InvalidDebugInfo; - - const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian); - - const address_size = try s.stream.readByte(); - if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; - - const compile_unit_pos = try s.seekable_stream.getPos(); - const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); - - try s.seekable_stream.seekTo(compile_unit_pos); - - const next_unit_pos = this_unit_offset + next_offset; - - while ((try s.seekable_stream.getPos()) < next_unit_pos) { - const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue; - const after_die_offset = try s.seekable_stream.getPos(); - - switch (die_obj.tag_id) { - DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => { - const fn_name = x: { - var depth: i32 = 3; - var this_die_obj = die_obj; - // Prenvent endless loops - while (depth > 0) : (depth -= 1) { - if (this_die_obj.getAttr(DW.AT_name)) |_| { - const name = try this_die_obj.getAttrString(di, DW.AT_name); - break :x name; - } else if (this_die_obj.getAttr(DW.AT_abstract_origin)) |ref| { - // Follow the DIE it points to and repeat - const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin); - if (ref_offset > next_offset) return error.InvalidDebugInfo; - try s.seekable_stream.seekTo(this_unit_offset + ref_offset); - this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; - } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| { - // Follow the DIE it points to and repeat - const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification); - if (ref_offset > next_offset) return error.InvalidDebugInfo; - try s.seekable_stream.seekTo(this_unit_offset + ref_offset); - this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; - } else { - break :x null; - } - } - - break :x null; - }; - - const pc_range = x: { - if (die_obj.getAttrAddr(DW.AT_low_pc)) |low_pc| { - if (die_obj.getAttr(DW.AT_high_pc)) |high_pc_value| { - const pc_end = switch (high_pc_value.*) { - FormValue.Address => |value| value, - FormValue.Const => |value| b: { - const offset = try value.asUnsignedLe(); - break :b (low_pc + offset); - }, - else => return error.InvalidDebugInfo, - }; - break :x PcRange{ - .start = low_pc, - .end = pc_end, - }; - } else { - break :x null; - } - } else |err| { - if (err != error.MissingDebugInfo) return err; - break :x null; - } - }; - - try di.func_list.append(Func{ - .name = fn_name, - .pc_range = pc_range, - }); - }, - else => {}, - } - - try s.seekable_stream.seekTo(after_die_offset); - } - - this_unit_offset += next_offset; - } - } - - fn scanAllCompileUnits(di: *DwarfInfo) !void { - var s = io.SliceSeekableInStream.init(di.debug_info); - var this_unit_offset: u64 = 0; - - while (true) { - s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) { - error.EndOfStream => return, - else => return err, - }; - - var is_64: bool = undefined; - const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64); - if (unit_length == 0) return; - const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); - - const version = try s.stream.readInt(u16, di.endian); - if (version < 2 or version > 5) return error.InvalidDebugInfo; - - const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian); - - const address_size = try s.stream.readByte(); - if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; - - const compile_unit_pos = try s.seekable_stream.getPos(); - const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); - - try s.seekable_stream.seekTo(compile_unit_pos); - - const compile_unit_die = try di.allocator().create(Die); - compile_unit_die.* = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; - - if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; - - const pc_range = x: { - if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| { - if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| { - const pc_end = switch (high_pc_value.*) { - FormValue.Address => |value| value, - FormValue.Const => |value| b: { - const offset = try value.asUnsignedLe(); - break :b (low_pc + offset); - }, - else => return error.InvalidDebugInfo, - }; - break :x PcRange{ - .start = low_pc, - .end = pc_end, - }; - } else { - break :x null; - } - } else |err| { - if (err != error.MissingDebugInfo) return err; - break :x null; - } - }; - - try di.compile_unit_list.append(CompileUnit{ - .version = version, - .is_64 = is_64, - .pc_range = pc_range, - .die = compile_unit_die, - }); - - this_unit_offset += next_offset; - } - } - - fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { - for (di.compile_unit_list.toSlice()) |*compile_unit| { - if (compile_unit.pc_range) |range| { - if (target_address >= range.start and target_address < range.end) return compile_unit; - } - if (di.debug_ranges) |debug_ranges| { - if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { - var s = io.SliceSeekableInStream.init(debug_ranges); - - // All the addresses in the list are relative to the value - // specified by DW_AT_low_pc or to some other value encoded - // in the list itself. - // If no starting value is specified use zero. - var base_address = compile_unit.die.getAttrAddr(DW.AT_low_pc) catch |err| switch (err) { - error.MissingDebugInfo => 0, - else => return err, - }; - - try s.seekable_stream.seekTo(ranges_offset); - - while (true) { - const begin_addr = try s.stream.readIntLittle(usize); - const end_addr = try s.stream.readIntLittle(usize); - if (begin_addr == 0 and end_addr == 0) { - break; - } - // This entry selects a new value for the base address - if (begin_addr == maxInt(usize)) { - base_address = end_addr; - continue; - } - if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) { - return compile_unit; - } - } - } else |err| { - if (err != error.MissingDebugInfo) return err; - continue; - } - } - } - return error.MissingDebugInfo; - } - - /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, - /// seeks in the stream and parses it. - fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { - for (di.abbrev_table_list.toSlice()) |*header| { - if (header.offset == abbrev_offset) { - return &header.table; - } - } - try di.abbrev_table_list.append(AbbrevTableHeader{ - .offset = abbrev_offset, - .table = try di.parseAbbrevTable(abbrev_offset), - }); - return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; - } - - fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable { - var s = io.SliceSeekableInStream.init(di.debug_abbrev); - - try s.seekable_stream.seekTo(offset); - var result = AbbrevTable.init(di.allocator()); - errdefer result.deinit(); - while (true) { - const abbrev_code = try leb.readULEB128(u64, &s.stream); - if (abbrev_code == 0) return result; - try result.append(AbbrevTableEntry{ - .abbrev_code = abbrev_code, - .tag_id = try leb.readULEB128(u64, &s.stream), - .has_children = (try s.stream.readByte()) == DW.CHILDREN_yes, - .attrs = ArrayList(AbbrevAttr).init(di.allocator()), - }); - const attrs = &result.items[result.len - 1].attrs; - - while (true) { - const attr_id = try leb.readULEB128(u64, &s.stream); - const form_id = try leb.readULEB128(u64, &s.stream); - if (attr_id == 0 and form_id == 0) break; - try attrs.append(AbbrevAttr{ - .attr_id = attr_id, - .form_id = form_id, - }); - } - } - } - - fn parseDie(di: *DwarfInfo, in_stream: var, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { - const abbrev_code = try leb.readULEB128(u64, in_stream); - if (abbrev_code == 0) return null; - const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; - - var result = Die{ - .tag_id = table_entry.tag_id, - .has_children = table_entry.has_children, - .attrs = ArrayList(Die.Attr).init(di.allocator()), - }; - try result.attrs.resize(table_entry.attrs.len); - for (table_entry.attrs.toSliceConst()) |attr, i| { - result.attrs.items[i] = Die.Attr{ - .id = attr.attr_id, - .value = try parseFormValue(di.allocator(), in_stream, attr.form_id, is_64), - }; - } - return result; - } - - fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo { - var s = io.SliceSeekableInStream.init(di.debug_line); - - const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir); - const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list); - - try s.seekable_stream.seekTo(line_info_offset); - - var is_64: bool = undefined; - const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64); - if (unit_length == 0) { - return error.MissingDebugInfo; - } - const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); - - const version = try s.stream.readInt(u16, di.endian); - // TODO support 3 and 5 - if (version != 2 and version != 4) return error.InvalidDebugInfo; - - const prologue_length = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian); - const prog_start_offset = (try s.seekable_stream.getPos()) + prologue_length; - - const minimum_instruction_length = try s.stream.readByte(); - if (minimum_instruction_length == 0) return error.InvalidDebugInfo; - - if (version >= 4) { - // maximum_operations_per_instruction - _ = try s.stream.readByte(); - } - - const default_is_stmt = (try s.stream.readByte()) != 0; - const line_base = try s.stream.readByteSigned(); - - const line_range = try s.stream.readByte(); - if (line_range == 0) return error.InvalidDebugInfo; - - const opcode_base = try s.stream.readByte(); - - const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); - - { - var i: usize = 0; - while (i < opcode_base - 1) : (i += 1) { - standard_opcode_lengths[i] = try s.stream.readByte(); - } - } - - var include_directories = ArrayList([]u8).init(di.allocator()); - try include_directories.append(compile_unit_cwd); - while (true) { - const dir = try readStringRaw(di.allocator(), &s.stream); - if (dir.len == 0) break; - try include_directories.append(dir); - } - - var file_entries = ArrayList(FileEntry).init(di.allocator()); - var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); - - while (true) { - const file_name = try readStringRaw(di.allocator(), &s.stream); - if (file_name.len == 0) break; - const dir_index = try leb.readULEB128(usize, &s.stream); - const mtime = try leb.readULEB128(usize, &s.stream); - const len_bytes = try leb.readULEB128(usize, &s.stream); - try file_entries.append(FileEntry{ - .file_name = file_name, - .dir_index = dir_index, - .mtime = mtime, - .len_bytes = len_bytes, - }); - } - - try s.seekable_stream.seekTo(prog_start_offset); - - const next_unit_pos = line_info_offset + next_offset; - - while ((try s.seekable_stream.getPos()) < next_unit_pos) { - const opcode = try s.stream.readByte(); - - if (opcode == DW.LNS_extended_op) { - const op_size = try leb.readULEB128(u64, &s.stream); - if (op_size < 1) return error.InvalidDebugInfo; - var sub_op = try s.stream.readByte(); - switch (sub_op) { - DW.LNE_end_sequence => { - prog.end_sequence = true; - if (try prog.checkLineMatch()) |info| return info; - prog.reset(); - }, - DW.LNE_set_address => { - const addr = try s.stream.readInt(usize, di.endian); - prog.address = addr; - }, - DW.LNE_define_file => { - const file_name = try readStringRaw(di.allocator(), &s.stream); - const dir_index = try leb.readULEB128(usize, &s.stream); - const mtime = try leb.readULEB128(usize, &s.stream); - const len_bytes = try leb.readULEB128(usize, &s.stream); - try file_entries.append(FileEntry{ - .file_name = file_name, - .dir_index = dir_index, - .mtime = mtime, - .len_bytes = len_bytes, - }); - }, - else => { - const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo; - try s.seekable_stream.seekBy(fwd_amt); - }, - } - } else if (opcode >= opcode_base) { - // special opcodes - const adjusted_opcode = opcode - opcode_base; - const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); - const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range); - prog.line += inc_line; - prog.address += inc_addr; - if (try prog.checkLineMatch()) |info| return info; - prog.basic_block = false; - } else { - switch (opcode) { - DW.LNS_copy => { - if (try prog.checkLineMatch()) |info| return info; - prog.basic_block = false; - }, - DW.LNS_advance_pc => { - const arg = try leb.readULEB128(usize, &s.stream); - prog.address += arg * minimum_instruction_length; - }, - DW.LNS_advance_line => { - const arg = try leb.readILEB128(i64, &s.stream); - prog.line += arg; - }, - DW.LNS_set_file => { - const arg = try leb.readULEB128(usize, &s.stream); - prog.file = arg; - }, - DW.LNS_set_column => { - const arg = try leb.readULEB128(u64, &s.stream); - prog.column = arg; - }, - DW.LNS_negate_stmt => { - prog.is_stmt = !prog.is_stmt; - }, - DW.LNS_set_basic_block => { - prog.basic_block = true; - }, - DW.LNS_const_add_pc => { - const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); - prog.address += inc_addr; - }, - DW.LNS_fixed_advance_pc => { - const arg = try s.stream.readInt(u16, di.endian); - prog.address += arg; - }, - DW.LNS_set_prologue_end => {}, - else => { - if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; - const len_bytes = standard_opcode_lengths[opcode - 1]; - try s.seekable_stream.seekBy(len_bytes); - }, - } - } - } - - return error.MissingDebugInfo; - } - - fn getString(di: *DwarfInfo, offset: u64) ![]u8 { - if (offset > di.debug_str.len) - return error.InvalidDebugInfo; - const casted_offset = math.cast(usize, offset) catch - return error.InvalidDebugInfo; - - // Valid strings always have a terminating zero byte - if (mem.indexOfScalarPos(u8, di.debug_str, casted_offset, 0)) |last| { - return di.debug_str[casted_offset..last]; - } - - return error.InvalidDebugInfo; - } -}; - pub const DebugInfo = switch (builtin.os) { .macosx, .ios, .watchos, .tvos => struct { symbols: []const MachoSymbol, @@ -1696,7 +1198,7 @@ pub const DebugInfo = switch (builtin.os) { const OFileTable = std.HashMap( *macho.nlist_64, - DwarfInfo, + DW.DwarfInfo, std.hash_map.getHashPtrAddrFn(*macho.nlist_64), std.hash_map.getTrivialEqlFn(*macho.nlist_64), ); @@ -1711,385 +1213,9 @@ pub const DebugInfo = switch (builtin.os) { sect_contribs: []pdb.SectionContribEntry, modules: []Module, }, - else => DwarfInfo, + else => DW.DwarfInfo, }; -const PcRange = struct { - start: u64, - end: u64, -}; - -const CompileUnit = struct { - version: u16, - is_64: bool, - die: *Die, - pc_range: ?PcRange, -}; - -const AbbrevTable = ArrayList(AbbrevTableEntry); - -const AbbrevTableHeader = struct { - // offset from .debug_abbrev - offset: u64, - table: AbbrevTable, -}; - -const AbbrevTableEntry = struct { - has_children: bool, - abbrev_code: u64, - tag_id: u64, - attrs: ArrayList(AbbrevAttr), -}; - -const AbbrevAttr = struct { - attr_id: u64, - form_id: u64, -}; - -const FormValue = union(enum) { - Address: u64, - Block: []u8, - Const: Constant, - ExprLoc: []u8, - Flag: bool, - SecOffset: u64, - Ref: u64, - RefAddr: u64, - String: []u8, - StrPtr: u64, -}; - -const Constant = struct { - payload: u64, - signed: bool, - - fn asUnsignedLe(self: *const Constant) !u64 { - if (self.signed) return error.InvalidDebugInfo; - return self.payload; - } -}; - -const Die = struct { - tag_id: u64, - has_children: bool, - attrs: ArrayList(Attr), - - const Attr = struct { - id: u64, - value: FormValue, - }; - - fn getAttr(self: *const Die, id: u64) ?*const FormValue { - for (self.attrs.toSliceConst()) |*attr| { - if (attr.id == id) return &attr.value; - } - return null; - } - - fn getAttrAddr(self: *const Die, id: u64) !u64 { - const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; - return switch (form_value.*) { - FormValue.Address => |value| value, - else => error.InvalidDebugInfo, - }; - } - - fn getAttrSecOffset(self: *const Die, id: u64) !u64 { - const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; - return switch (form_value.*) { - FormValue.Const => |value| value.asUnsignedLe(), - FormValue.SecOffset => |value| value, - else => error.InvalidDebugInfo, - }; - } - - fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 { - const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; - return switch (form_value.*) { - FormValue.Const => |value| value.asUnsignedLe(), - else => error.InvalidDebugInfo, - }; - } - - fn getAttrRef(self: *const Die, id: u64) !u64 { - const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; - return switch (form_value.*) { - FormValue.Ref => |value| value, - else => error.InvalidDebugInfo, - }; - } - - fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 { - const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; - return switch (form_value.*) { - FormValue.String => |value| value, - FormValue.StrPtr => |offset| di.getString(offset), - else => error.InvalidDebugInfo, - }; - } -}; - -const FileEntry = struct { - file_name: []const u8, - dir_index: usize, - mtime: usize, - len_bytes: usize, -}; - -pub const LineInfo = struct { - line: u64, - column: u64, - file_name: []const u8, - allocator: ?*mem.Allocator, - - fn deinit(self: LineInfo) void { - const allocator = self.allocator orelse return; - allocator.free(self.file_name); - } -}; - -const LineNumberProgram = struct { - address: usize, - file: usize, - line: i64, - column: u64, - is_stmt: bool, - basic_block: bool, - end_sequence: bool, - - default_is_stmt: bool, - target_address: usize, - include_dirs: []const []const u8, - file_entries: *ArrayList(FileEntry), - - prev_address: usize, - prev_file: usize, - prev_line: i64, - prev_column: u64, - prev_is_stmt: bool, - prev_basic_block: bool, - prev_end_sequence: bool, - - // Reset the state machine following the DWARF specification - pub fn reset(self: *LineNumberProgram) void { - self.address = 0; - self.file = 1; - self.line = 1; - self.column = 0; - self.is_stmt = self.default_is_stmt; - self.basic_block = false; - self.end_sequence = false; - // Invalidate all the remaining fields - self.prev_address = 0; - self.prev_file = undefined; - self.prev_line = undefined; - self.prev_column = undefined; - self.prev_is_stmt = undefined; - self.prev_basic_block = undefined; - self.prev_end_sequence = undefined; - } - - pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram { - return LineNumberProgram{ - .address = 0, - .file = 1, - .line = 1, - .column = 0, - .is_stmt = is_stmt, - .basic_block = false, - .end_sequence = false, - .include_dirs = include_dirs, - .file_entries = file_entries, - .default_is_stmt = is_stmt, - .target_address = target_address, - .prev_address = 0, - .prev_file = undefined, - .prev_line = undefined, - .prev_column = undefined, - .prev_is_stmt = undefined, - .prev_basic_block = undefined, - .prev_end_sequence = undefined, - }; - } - - pub 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 = try fs.path.join(self.file_entries.allocator, &[_][]const u8{ dir_name, file_entry.file_name }); - errdefer self.file_entries.allocator.free(file_name); - return LineInfo{ - .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0, - .column = self.prev_column, - .file_name = file_name, - .allocator = self.file_entries.allocator, - }; - } - - 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; - } -}; - -// TODO the noasyncs here are workarounds -fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 { - var buf = ArrayList(u8).init(allocator); - while (true) { - const byte = try noasync in_stream.readByte(); - if (byte == 0) break; - try buf.append(byte); - } - return buf.toSlice(); -} - -// TODO the noasyncs here are workarounds -fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { - const buf = try allocator.alloc(u8, size); - errdefer allocator.free(buf); - if ((try noasync in_stream.read(buf)) < size) return error.EndOfFile; - return buf; -} - -fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { - const buf = try readAllocBytes(allocator, in_stream, size); - return FormValue{ .Block = buf }; -} - -// TODO the noasyncs here are workarounds -fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { - const block_len = try noasync in_stream.readVarInt(usize, builtin.Endian.Little, size); - return parseFormValueBlockLen(allocator, in_stream, block_len); -} - -fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, comptime size: i32) !FormValue { - // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here. - // `noasync` should be removed from all the function calls once it is fixed. - return FormValue{ - .Const = Constant{ - .signed = signed, - .payload = switch (size) { - 1 => try noasync in_stream.readIntLittle(u8), - 2 => try noasync in_stream.readIntLittle(u16), - 4 => try noasync in_stream.readIntLittle(u32), - 8 => try noasync in_stream.readIntLittle(u64), - -1 => blk: { - if (signed) { - const x = try noasync leb.readILEB128(i64, in_stream); - break :blk @bitCast(u64, x); - } else { - const x = try noasync leb.readULEB128(u64, in_stream); - break :blk x; - } - }, - else => @compileError("Invalid size"), - }, - }, - }; -} - -// TODO the noasyncs here are workarounds -fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 { - return if (is_64) try noasync in_stream.readIntLittle(u64) else @as(u64, try noasync in_stream.readIntLittle(u32)); -} - -// TODO the noasyncs here are workarounds -fn parseFormValueTargetAddrSize(in_stream: var) !u64 { - if (@sizeOf(usize) == 4) { - // TODO this cast should not be needed - return @as(u64, try noasync in_stream.readIntLittle(u32)); - } else if (@sizeOf(usize) == 8) { - return noasync in_stream.readIntLittle(u64); - } else { - unreachable; - } -} - -// TODO the noasyncs here are workarounds -fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, size: i32) !FormValue { - return FormValue{ - .Ref = switch (size) { - 1 => try noasync in_stream.readIntLittle(u8), - 2 => try noasync in_stream.readIntLittle(u16), - 4 => try noasync in_stream.readIntLittle(u32), - 8 => try noasync in_stream.readIntLittle(u64), - -1 => try noasync leb.readULEB128(u64, in_stream), - else => unreachable, - }, - }; -} - -// TODO the noasyncs here are workarounds -fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue { - return switch (form_id) { - DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) }, - DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), - DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2), - DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4), - DW.FORM_block => x: { - const block_len = try noasync leb.readULEB128(usize, in_stream); - return parseFormValueBlockLen(allocator, in_stream, block_len); - }, - DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1), - DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2), - DW.FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4), - DW.FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8), - DW.FORM_udata, DW.FORM_sdata => { - const signed = form_id == DW.FORM_sdata; - return parseFormValueConstant(allocator, in_stream, signed, -1); - }, - DW.FORM_exprloc => { - const size = try noasync leb.readULEB128(usize, in_stream); - const buf = try readAllocBytes(allocator, in_stream, size); - return FormValue{ .ExprLoc = buf }; - }, - DW.FORM_flag => FormValue{ .Flag = (try noasync in_stream.readByte()) != 0 }, - DW.FORM_flag_present => FormValue{ .Flag = true }, - DW.FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, - - DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, 1), - DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, 2), - DW.FORM_ref4 => parseFormValueRef(allocator, in_stream, 4), - DW.FORM_ref8 => parseFormValueRef(allocator, in_stream, 8), - DW.FORM_ref_udata => parseFormValueRef(allocator, in_stream, -1), - - DW.FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, - DW.FORM_ref_sig8 => FormValue{ .Ref = try noasync in_stream.readIntLittle(u64) }, - - DW.FORM_string => FormValue{ .String = try readStringRaw(allocator, in_stream) }, - DW.FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, - DW.FORM_indirect => { - const child_form_id = try noasync leb.readULEB128(u64, in_stream); - const F = @TypeOf(async parseFormValue(allocator, in_stream, child_form_id, is_64)); - var frame = try allocator.create(F); - defer allocator.destroy(frame); - return await @asyncCall(frame, {}, parseFormValue, allocator, in_stream, child_form_id, is_64); - }, - else => error.InvalidDebugInfo, - }; -} - -fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { - for (abbrev_table.toSliceConst()) |*table_entry| { - if (table_entry.abbrev_code == abbrev_code) return table_entry; - } - return null; -} - /// TODO resources https://github.com/ziglang/zig/issues/4353 fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, address: usize) !LineInfo { const ofile = symbol.ofile orelse return error.MissingDebugInfo; @@ -2175,7 +1301,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, address: usize) ! var debug_abbrev = opt_debug_abbrev orelse return error.MissingDebugInfo; - gop.kv.value = DwarfInfo{ + gop.kv.value = DW.DwarfInfo{ .endian = .Little, .debug_info = exe_mmap[@intCast(usize, debug_info.offset)..@intCast(usize, debug_info.offset + debug_info.size)], .debug_abbrev = exe_mmap[@intCast(usize, debug_abbrev.offset)..@intCast(usize, debug_abbrev.offset + debug_abbrev.size)], @@ -2186,7 +1312,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, address: usize) ! else null, }; - try openDwarfDebugInfo(&gop.kv.value, di.allocator()); + try DW.openDwarfDebugInfo(&gop.kv.value, di.allocator()); break :blk &gop.kv.value; }; @@ -2196,23 +1322,6 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, address: usize) ! return dwarf_info.getLineNumberInfo(compile_unit.*, o_file_address); } -const Func = struct { - pc_range: ?PcRange, - name: ?[]u8, -}; - -fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 { - const first_32_bits = try in_stream.readIntLittle(u32); - is_64.* = (first_32_bits == 0xffffffff); - if (is_64.*) { - return in_stream.readIntLittle(u64); - } else { - if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; - // TODO this cast should not be needed - return @as(u64, first_32_bits); - } -} - /// TODO multithreaded awareness var debug_info_allocator: ?*mem.Allocator = null; var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined; diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index 2f3b29302d..cb5bb3ec8a 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -1,682 +1,889 @@ -pub const TAG_padding = 0x00; -pub const TAG_array_type = 0x01; -pub const TAG_class_type = 0x02; -pub const TAG_entry_point = 0x03; -pub const TAG_enumeration_type = 0x04; -pub const TAG_formal_parameter = 0x05; -pub const TAG_imported_declaration = 0x08; -pub const TAG_label = 0x0a; -pub const TAG_lexical_block = 0x0b; -pub const TAG_member = 0x0d; -pub const TAG_pointer_type = 0x0f; -pub const TAG_reference_type = 0x10; -pub const TAG_compile_unit = 0x11; -pub const TAG_string_type = 0x12; -pub const TAG_structure_type = 0x13; -pub const TAG_subroutine = 0x14; -pub const TAG_subroutine_type = 0x15; -pub const TAG_typedef = 0x16; -pub const TAG_union_type = 0x17; -pub const TAG_unspecified_parameters = 0x18; -pub const TAG_variant = 0x19; -pub const TAG_common_block = 0x1a; -pub const TAG_common_inclusion = 0x1b; -pub const TAG_inheritance = 0x1c; -pub const TAG_inlined_subroutine = 0x1d; -pub const TAG_module = 0x1e; -pub const TAG_ptr_to_member_type = 0x1f; -pub const TAG_set_type = 0x20; -pub const TAG_subrange_type = 0x21; -pub const TAG_with_stmt = 0x22; -pub const TAG_access_declaration = 0x23; -pub const TAG_base_type = 0x24; -pub const TAG_catch_block = 0x25; -pub const TAG_const_type = 0x26; -pub const TAG_constant = 0x27; -pub const TAG_enumerator = 0x28; -pub const TAG_file_type = 0x29; -pub const TAG_friend = 0x2a; -pub const TAG_namelist = 0x2b; -pub const TAG_namelist_item = 0x2c; -pub const TAG_packed_type = 0x2d; -pub const TAG_subprogram = 0x2e; -pub const TAG_template_type_param = 0x2f; -pub const TAG_template_value_param = 0x30; -pub const TAG_thrown_type = 0x31; -pub const TAG_try_block = 0x32; -pub const TAG_variant_part = 0x33; -pub const TAG_variable = 0x34; -pub const TAG_volatile_type = 0x35; +const std = @import("std.zig"); +const builtin = @import("builtin"); +const debug = std.debug; +const fs = std.fs; +const io = std.io; +const mem = std.mem; +const math = std.math; +const leb = @import("debug/leb128.zig"); -// DWARF 3 -pub const TAG_dwarf_procedure = 0x36; -pub const TAG_restrict_type = 0x37; -pub const TAG_interface_type = 0x38; -pub const TAG_namespace = 0x39; -pub const TAG_imported_module = 0x3a; -pub const TAG_unspecified_type = 0x3b; -pub const TAG_partial_unit = 0x3c; -pub const TAG_imported_unit = 0x3d; -pub const TAG_condition = 0x3f; -pub const TAG_shared_type = 0x40; +const ArrayList = std.ArrayList; -// DWARF 4 -pub const TAG_type_unit = 0x41; -pub const TAG_rvalue_reference_type = 0x42; -pub const TAG_template_alias = 0x43; +usingnamespace @import("dwarf_bits.zig"); -pub const TAG_lo_user = 0x4080; -pub const TAG_hi_user = 0xffff; +pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror); +pub const DwarfInStream = io.InStream(anyerror); -// SGI/MIPS Extensions. -pub const DW_TAG_MIPS_loop = 0x4081; +const PcRange = struct { + start: u64, + end: u64, +}; -// HP extensions. See: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz . -pub const TAG_HP_array_descriptor = 0x4090; -pub const TAG_HP_Bliss_field = 0x4091; -pub const TAG_HP_Bliss_field_set = 0x4092; +const Func = struct { + pc_range: ?PcRange, + name: ?[]u8, +}; -// GNU extensions. -pub const TAG_format_label = 0x4101; // For FORTRAN 77 and Fortran 90. -pub const TAG_function_template = 0x4102; // For C++. -pub const TAG_class_template = 0x4103; //For C++. -pub const TAG_GNU_BINCL = 0x4104; -pub const TAG_GNU_EINCL = 0x4105; +const CompileUnit = struct { + version: u16, + is_64: bool, + die: *Die, + pc_range: ?PcRange, +}; -// Template template parameter. -// See http://gcc.gnu.org/wiki/TemplateParmsDwarf . -pub const TAG_GNU_template_template_param = 0x4106; +const AbbrevTable = ArrayList(AbbrevTableEntry); -// Template parameter pack extension = specified at -// http://wiki.dwarfstd.org/index.php?title=C%2B%2B0x:_Variadic_templates -// The values of these two TAGS are in the DW_TAG_GNU_* space until the tags -// are properly part of DWARF 5. -pub const TAG_GNU_template_parameter_pack = 0x4107; -pub const TAG_GNU_formal_parameter_pack = 0x4108; -// The GNU call site extension = specified at -// http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open . -// The values of these two TAGS are in the DW_TAG_GNU_* space until the tags -// are properly part of DWARF 5. -pub const TAG_GNU_call_site = 0x4109; -pub const TAG_GNU_call_site_parameter = 0x410a; -// Extensions for UPC. See: http://dwarfstd.org/doc/DWARF4.pdf. -pub const TAG_upc_shared_type = 0x8765; -pub const TAG_upc_strict_type = 0x8766; -pub const TAG_upc_relaxed_type = 0x8767; -// PGI (STMicroelectronics; extensions. No documentation available. -pub const TAG_PGI_kanji_type = 0xA000; -pub const TAG_PGI_interface_block = 0xA020; +const AbbrevTableHeader = struct { + // offset from .debug_abbrev + offset: u64, + table: AbbrevTable, +}; -pub const FORM_addr = 0x01; -pub const FORM_block2 = 0x03; -pub const FORM_block4 = 0x04; -pub const FORM_data2 = 0x05; -pub const FORM_data4 = 0x06; -pub const FORM_data8 = 0x07; -pub const FORM_string = 0x08; -pub const FORM_block = 0x09; -pub const FORM_block1 = 0x0a; -pub const FORM_data1 = 0x0b; -pub const FORM_flag = 0x0c; -pub const FORM_sdata = 0x0d; -pub const FORM_strp = 0x0e; -pub const FORM_udata = 0x0f; -pub const FORM_ref_addr = 0x10; -pub const FORM_ref1 = 0x11; -pub const FORM_ref2 = 0x12; -pub const FORM_ref4 = 0x13; -pub const FORM_ref8 = 0x14; -pub const FORM_ref_udata = 0x15; -pub const FORM_indirect = 0x16; -pub const FORM_sec_offset = 0x17; -pub const FORM_exprloc = 0x18; -pub const FORM_flag_present = 0x19; -pub const FORM_ref_sig8 = 0x20; +const AbbrevTableEntry = struct { + has_children: bool, + abbrev_code: u64, + tag_id: u64, + attrs: ArrayList(AbbrevAttr), +}; -// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission. -pub const FORM_GNU_addr_index = 0x1f01; -pub const FORM_GNU_str_index = 0x1f02; +const AbbrevAttr = struct { + attr_id: u64, + form_id: u64, +}; -// Extensions for DWZ multifile. -// See http://www.dwarfstd.org/ShowIssue.php?issue=120604.1&type=open . -pub const FORM_GNU_ref_alt = 0x1f20; -pub const FORM_GNU_strp_alt = 0x1f21; +const FormValue = union(enum) { + Address: u64, + Block: []u8, + Const: Constant, + ExprLoc: []u8, + Flag: bool, + SecOffset: u64, + Ref: u64, + RefAddr: u64, + String: []u8, + StrPtr: u64, +}; -pub const AT_sibling = 0x01; -pub const AT_location = 0x02; -pub const AT_name = 0x03; -pub const AT_ordering = 0x09; -pub const AT_subscr_data = 0x0a; -pub const AT_byte_size = 0x0b; -pub const AT_bit_offset = 0x0c; -pub const AT_bit_size = 0x0d; -pub const AT_element_list = 0x0f; -pub const AT_stmt_list = 0x10; -pub const AT_low_pc = 0x11; -pub const AT_high_pc = 0x12; -pub const AT_language = 0x13; -pub const AT_member = 0x14; -pub const AT_discr = 0x15; -pub const AT_discr_value = 0x16; -pub const AT_visibility = 0x17; -pub const AT_import = 0x18; -pub const AT_string_length = 0x19; -pub const AT_common_reference = 0x1a; -pub const AT_comp_dir = 0x1b; -pub const AT_const_value = 0x1c; -pub const AT_containing_type = 0x1d; -pub const AT_default_value = 0x1e; -pub const AT_inline = 0x20; -pub const AT_is_optional = 0x21; -pub const AT_lower_bound = 0x22; -pub const AT_producer = 0x25; -pub const AT_prototyped = 0x27; -pub const AT_return_addr = 0x2a; -pub const AT_start_scope = 0x2c; -pub const AT_bit_stride = 0x2e; -pub const AT_upper_bound = 0x2f; -pub const AT_abstract_origin = 0x31; -pub const AT_accessibility = 0x32; -pub const AT_address_class = 0x33; -pub const AT_artificial = 0x34; -pub const AT_base_types = 0x35; -pub const AT_calling_convention = 0x36; -pub const AT_count = 0x37; -pub const AT_data_member_location = 0x38; -pub const AT_decl_column = 0x39; -pub const AT_decl_file = 0x3a; -pub const AT_decl_line = 0x3b; -pub const AT_declaration = 0x3c; -pub const AT_discr_list = 0x3d; -pub const AT_encoding = 0x3e; -pub const AT_external = 0x3f; -pub const AT_frame_base = 0x40; -pub const AT_friend = 0x41; -pub const AT_identifier_case = 0x42; -pub const AT_macro_info = 0x43; -pub const AT_namelist_items = 0x44; -pub const AT_priority = 0x45; -pub const AT_segment = 0x46; -pub const AT_specification = 0x47; -pub const AT_static_link = 0x48; -pub const AT_type = 0x49; -pub const AT_use_location = 0x4a; -pub const AT_variable_parameter = 0x4b; -pub const AT_virtuality = 0x4c; -pub const AT_vtable_elem_location = 0x4d; +const Constant = struct { + payload: u64, + signed: bool, -// DWARF 3 values. -pub const AT_allocated = 0x4e; -pub const AT_associated = 0x4f; -pub const AT_data_location = 0x50; -pub const AT_byte_stride = 0x51; -pub const AT_entry_pc = 0x52; -pub const AT_use_UTF8 = 0x53; -pub const AT_extension = 0x54; -pub const AT_ranges = 0x55; -pub const AT_trampoline = 0x56; -pub const AT_call_column = 0x57; -pub const AT_call_file = 0x58; -pub const AT_call_line = 0x59; -pub const AT_description = 0x5a; -pub const AT_binary_scale = 0x5b; -pub const AT_decimal_scale = 0x5c; -pub const AT_small = 0x5d; -pub const AT_decimal_sign = 0x5e; -pub const AT_digit_count = 0x5f; -pub const AT_picture_string = 0x60; -pub const AT_mutable = 0x61; -pub const AT_threads_scaled = 0x62; -pub const AT_explicit = 0x63; -pub const AT_object_pointer = 0x64; -pub const AT_endianity = 0x65; -pub const AT_elemental = 0x66; -pub const AT_pure = 0x67; -pub const AT_recursive = 0x68; + fn asUnsignedLe(self: *const Constant) !u64 { + if (self.signed) return error.InvalidDebugInfo; + return self.payload; + } +}; -// DWARF 4. -pub const AT_signature = 0x69; -pub const AT_main_subprogram = 0x6a; -pub const AT_data_bit_offset = 0x6b; -pub const AT_const_expr = 0x6c; -pub const AT_enum_class = 0x6d; -pub const AT_linkage_name = 0x6e; +const Die = struct { + tag_id: u64, + has_children: bool, + attrs: ArrayList(Attr), -// DWARF 5 -pub const AT_alignment = 0x88; + const Attr = struct { + id: u64, + value: FormValue, + }; -pub const AT_lo_user = 0x2000; // Implementation-defined range start. -pub const AT_hi_user = 0x3fff; // Implementation-defined range end. + fn getAttr(self: *const Die, id: u64) ?*const FormValue { + for (self.attrs.toSliceConst()) |*attr| { + if (attr.id == id) return &attr.value; + } + return null; + } -// SGI/MIPS extensions. -pub const AT_MIPS_fde = 0x2001; -pub const AT_MIPS_loop_begin = 0x2002; -pub const AT_MIPS_tail_loop_begin = 0x2003; -pub const AT_MIPS_epilog_begin = 0x2004; -pub const AT_MIPS_loop_unroll_factor = 0x2005; -pub const AT_MIPS_software_pipeline_depth = 0x2006; -pub const AT_MIPS_linkage_name = 0x2007; -pub const AT_MIPS_stride = 0x2008; -pub const AT_MIPS_abstract_name = 0x2009; -pub const AT_MIPS_clone_origin = 0x200a; -pub const AT_MIPS_has_inlines = 0x200b; + fn getAttrAddr(self: *const Die, id: u64) !u64 { + const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; + return switch (form_value.*) { + FormValue.Address => |value| value, + else => error.InvalidDebugInfo, + }; + } -// HP extensions. -pub const AT_HP_block_index = 0x2000; -pub const AT_HP_unmodifiable = 0x2001; // Same as DW_AT_MIPS_fde. -pub const AT_HP_prologue = 0x2005; // Same as DW_AT_MIPS_loop_unroll. -pub const AT_HP_epilogue = 0x2008; // Same as DW_AT_MIPS_stride. -pub const AT_HP_actuals_stmt_list = 0x2010; -pub const AT_HP_proc_per_section = 0x2011; -pub const AT_HP_raw_data_ptr = 0x2012; -pub const AT_HP_pass_by_reference = 0x2013; -pub const AT_HP_opt_level = 0x2014; -pub const AT_HP_prof_version_id = 0x2015; -pub const AT_HP_opt_flags = 0x2016; -pub const AT_HP_cold_region_low_pc = 0x2017; -pub const AT_HP_cold_region_high_pc = 0x2018; -pub const AT_HP_all_variables_modifiable = 0x2019; -pub const AT_HP_linkage_name = 0x201a; -pub const AT_HP_prof_flags = 0x201b; // In comp unit of procs_info for -g. -pub const AT_HP_unit_name = 0x201f; -pub const AT_HP_unit_size = 0x2020; -pub const AT_HP_widened_byte_size = 0x2021; -pub const AT_HP_definition_points = 0x2022; -pub const AT_HP_default_location = 0x2023; -pub const AT_HP_is_result_param = 0x2029; + fn getAttrSecOffset(self: *const Die, id: u64) !u64 { + const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; + return switch (form_value.*) { + FormValue.Const => |value| value.asUnsignedLe(), + FormValue.SecOffset => |value| value, + else => error.InvalidDebugInfo, + }; + } -// GNU extensions. -pub const AT_sf_names = 0x2101; -pub const AT_src_info = 0x2102; -pub const AT_mac_info = 0x2103; -pub const AT_src_coords = 0x2104; -pub const AT_body_begin = 0x2105; -pub const AT_body_end = 0x2106; -pub const AT_GNU_vector = 0x2107; -// Thread-safety annotations. -// See http://gcc.gnu.org/wiki/ThreadSafetyAnnotation . -pub const AT_GNU_guarded_by = 0x2108; -pub const AT_GNU_pt_guarded_by = 0x2109; -pub const AT_GNU_guarded = 0x210a; -pub const AT_GNU_pt_guarded = 0x210b; -pub const AT_GNU_locks_excluded = 0x210c; -pub const AT_GNU_exclusive_locks_required = 0x210d; -pub const AT_GNU_shared_locks_required = 0x210e; -// One-definition rule violation detection. -// See http://gcc.gnu.org/wiki/DwarfSeparateTypeInfo . -pub const AT_GNU_odr_signature = 0x210f; -// Template template argument name. -// See http://gcc.gnu.org/wiki/TemplateParmsDwarf . -pub const AT_GNU_template_name = 0x2110; -// The GNU call site extension. -// See http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open . -pub const AT_GNU_call_site_value = 0x2111; -pub const AT_GNU_call_site_data_value = 0x2112; -pub const AT_GNU_call_site_target = 0x2113; -pub const AT_GNU_call_site_target_clobbered = 0x2114; -pub const AT_GNU_tail_call = 0x2115; -pub const AT_GNU_all_tail_call_sites = 0x2116; -pub const AT_GNU_all_call_sites = 0x2117; -pub const AT_GNU_all_source_call_sites = 0x2118; -// Section offset into .debug_macro section. -pub const AT_GNU_macros = 0x2119; -// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission. -pub const AT_GNU_dwo_name = 0x2130; -pub const AT_GNU_dwo_id = 0x2131; -pub const AT_GNU_ranges_base = 0x2132; -pub const AT_GNU_addr_base = 0x2133; -pub const AT_GNU_pubnames = 0x2134; -pub const AT_GNU_pubtypes = 0x2135; -// VMS extensions. -pub const AT_VMS_rtnbeg_pd_address = 0x2201; -// GNAT extensions. -// GNAT descriptive type. -// See http://gcc.gnu.org/wiki/DW_AT_GNAT_descriptive_type . -pub const AT_use_GNAT_descriptive_type = 0x2301; -pub const AT_GNAT_descriptive_type = 0x2302; -// UPC extension. -pub const AT_upc_threads_scaled = 0x3210; -// PGI (STMicroelectronics) extensions. -pub const AT_PGI_lbase = 0x3a00; -pub const AT_PGI_soffset = 0x3a01; -pub const AT_PGI_lstride = 0x3a02; + fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 { + const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; + return switch (form_value.*) { + FormValue.Const => |value| value.asUnsignedLe(), + else => error.InvalidDebugInfo, + }; + } -pub const OP_addr = 0x03; -pub const OP_deref = 0x06; -pub const OP_const1u = 0x08; -pub const OP_const1s = 0x09; -pub const OP_const2u = 0x0a; -pub const OP_const2s = 0x0b; -pub const OP_const4u = 0x0c; -pub const OP_const4s = 0x0d; -pub const OP_const8u = 0x0e; -pub const OP_const8s = 0x0f; -pub const OP_constu = 0x10; -pub const OP_consts = 0x11; -pub const OP_dup = 0x12; -pub const OP_drop = 0x13; -pub const OP_over = 0x14; -pub const OP_pick = 0x15; -pub const OP_swap = 0x16; -pub const OP_rot = 0x17; -pub const OP_xderef = 0x18; -pub const OP_abs = 0x19; -pub const OP_and = 0x1a; -pub const OP_div = 0x1b; -pub const OP_minus = 0x1c; -pub const OP_mod = 0x1d; -pub const OP_mul = 0x1e; -pub const OP_neg = 0x1f; -pub const OP_not = 0x20; -pub const OP_or = 0x21; -pub const OP_plus = 0x22; -pub const OP_plus_uconst = 0x23; -pub const OP_shl = 0x24; -pub const OP_shr = 0x25; -pub const OP_shra = 0x26; -pub const OP_xor = 0x27; -pub const OP_bra = 0x28; -pub const OP_eq = 0x29; -pub const OP_ge = 0x2a; -pub const OP_gt = 0x2b; -pub const OP_le = 0x2c; -pub const OP_lt = 0x2d; -pub const OP_ne = 0x2e; -pub const OP_skip = 0x2f; -pub const OP_lit0 = 0x30; -pub const OP_lit1 = 0x31; -pub const OP_lit2 = 0x32; -pub const OP_lit3 = 0x33; -pub const OP_lit4 = 0x34; -pub const OP_lit5 = 0x35; -pub const OP_lit6 = 0x36; -pub const OP_lit7 = 0x37; -pub const OP_lit8 = 0x38; -pub const OP_lit9 = 0x39; -pub const OP_lit10 = 0x3a; -pub const OP_lit11 = 0x3b; -pub const OP_lit12 = 0x3c; -pub const OP_lit13 = 0x3d; -pub const OP_lit14 = 0x3e; -pub const OP_lit15 = 0x3f; -pub const OP_lit16 = 0x40; -pub const OP_lit17 = 0x41; -pub const OP_lit18 = 0x42; -pub const OP_lit19 = 0x43; -pub const OP_lit20 = 0x44; -pub const OP_lit21 = 0x45; -pub const OP_lit22 = 0x46; -pub const OP_lit23 = 0x47; -pub const OP_lit24 = 0x48; -pub const OP_lit25 = 0x49; -pub const OP_lit26 = 0x4a; -pub const OP_lit27 = 0x4b; -pub const OP_lit28 = 0x4c; -pub const OP_lit29 = 0x4d; -pub const OP_lit30 = 0x4e; -pub const OP_lit31 = 0x4f; -pub const OP_reg0 = 0x50; -pub const OP_reg1 = 0x51; -pub const OP_reg2 = 0x52; -pub const OP_reg3 = 0x53; -pub const OP_reg4 = 0x54; -pub const OP_reg5 = 0x55; -pub const OP_reg6 = 0x56; -pub const OP_reg7 = 0x57; -pub const OP_reg8 = 0x58; -pub const OP_reg9 = 0x59; -pub const OP_reg10 = 0x5a; -pub const OP_reg11 = 0x5b; -pub const OP_reg12 = 0x5c; -pub const OP_reg13 = 0x5d; -pub const OP_reg14 = 0x5e; -pub const OP_reg15 = 0x5f; -pub const OP_reg16 = 0x60; -pub const OP_reg17 = 0x61; -pub const OP_reg18 = 0x62; -pub const OP_reg19 = 0x63; -pub const OP_reg20 = 0x64; -pub const OP_reg21 = 0x65; -pub const OP_reg22 = 0x66; -pub const OP_reg23 = 0x67; -pub const OP_reg24 = 0x68; -pub const OP_reg25 = 0x69; -pub const OP_reg26 = 0x6a; -pub const OP_reg27 = 0x6b; -pub const OP_reg28 = 0x6c; -pub const OP_reg29 = 0x6d; -pub const OP_reg30 = 0x6e; -pub const OP_reg31 = 0x6f; -pub const OP_breg0 = 0x70; -pub const OP_breg1 = 0x71; -pub const OP_breg2 = 0x72; -pub const OP_breg3 = 0x73; -pub const OP_breg4 = 0x74; -pub const OP_breg5 = 0x75; -pub const OP_breg6 = 0x76; -pub const OP_breg7 = 0x77; -pub const OP_breg8 = 0x78; -pub const OP_breg9 = 0x79; -pub const OP_breg10 = 0x7a; -pub const OP_breg11 = 0x7b; -pub const OP_breg12 = 0x7c; -pub const OP_breg13 = 0x7d; -pub const OP_breg14 = 0x7e; -pub const OP_breg15 = 0x7f; -pub const OP_breg16 = 0x80; -pub const OP_breg17 = 0x81; -pub const OP_breg18 = 0x82; -pub const OP_breg19 = 0x83; -pub const OP_breg20 = 0x84; -pub const OP_breg21 = 0x85; -pub const OP_breg22 = 0x86; -pub const OP_breg23 = 0x87; -pub const OP_breg24 = 0x88; -pub const OP_breg25 = 0x89; -pub const OP_breg26 = 0x8a; -pub const OP_breg27 = 0x8b; -pub const OP_breg28 = 0x8c; -pub const OP_breg29 = 0x8d; -pub const OP_breg30 = 0x8e; -pub const OP_breg31 = 0x8f; -pub const OP_regx = 0x90; -pub const OP_fbreg = 0x91; -pub const OP_bregx = 0x92; -pub const OP_piece = 0x93; -pub const OP_deref_size = 0x94; -pub const OP_xderef_size = 0x95; -pub const OP_nop = 0x96; + fn getAttrRef(self: *const Die, id: u64) !u64 { + const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; + return switch (form_value.*) { + FormValue.Ref => |value| value, + else => error.InvalidDebugInfo, + }; + } -// DWARF 3 extensions. -pub const OP_push_object_address = 0x97; -pub const OP_call2 = 0x98; -pub const OP_call4 = 0x99; -pub const OP_call_ref = 0x9a; -pub const OP_form_tls_address = 0x9b; -pub const OP_call_frame_cfa = 0x9c; -pub const OP_bit_piece = 0x9d; + fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 { + const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; + return switch (form_value.*) { + FormValue.String => |value| value, + FormValue.StrPtr => |offset| di.getString(offset), + else => error.InvalidDebugInfo, + }; + } +}; -// DWARF 4 extensions. -pub const OP_implicit_value = 0x9e; -pub const OP_stack_value = 0x9f; +const FileEntry = struct { + file_name: []const u8, + dir_index: usize, + mtime: usize, + len_bytes: usize, +}; -pub const OP_lo_user = 0xe0; // Implementation-defined range start. -pub const OP_hi_user = 0xff; // Implementation-defined range end. +const LineNumberProgram = struct { + address: usize, + file: usize, + line: i64, + column: u64, + is_stmt: bool, + basic_block: bool, + end_sequence: bool, -// GNU extensions. -pub const OP_GNU_push_tls_address = 0xe0; -// The following is for marking variables that are uninitialized. -pub const OP_GNU_uninit = 0xf0; -pub const OP_GNU_encoded_addr = 0xf1; -// The GNU implicit pointer extension. -// See http://www.dwarfstd.org/ShowIssue.php?issue=100831.1&type=open . -pub const OP_GNU_implicit_pointer = 0xf2; -// The GNU entry value extension. -// See http://www.dwarfstd.org/ShowIssue.php?issue=100909.1&type=open . -pub const OP_GNU_entry_value = 0xf3; -// The GNU typed stack extension. -// See http://www.dwarfstd.org/doc/040408.1.html . -pub const OP_GNU_const_type = 0xf4; -pub const OP_GNU_regval_type = 0xf5; -pub const OP_GNU_deref_type = 0xf6; -pub const OP_GNU_convert = 0xf7; -pub const OP_GNU_reinterpret = 0xf9; -// The GNU parameter ref extension. -pub const OP_GNU_parameter_ref = 0xfa; -// Extension for Fission. See http://gcc.gnu.org/wiki/DebugFission. -pub const OP_GNU_addr_index = 0xfb; -pub const OP_GNU_const_index = 0xfc; -// HP extensions. -pub const OP_HP_unknown = 0xe0; // Ouch, the same as GNU_push_tls_address. -pub const OP_HP_is_value = 0xe1; -pub const OP_HP_fltconst4 = 0xe2; -pub const OP_HP_fltconst8 = 0xe3; -pub const OP_HP_mod_range = 0xe4; -pub const OP_HP_unmod_range = 0xe5; -pub const OP_HP_tls = 0xe6; -// PGI (STMicroelectronics) extensions. -pub const OP_PGI_omp_thread_num = 0xf8; + default_is_stmt: bool, + target_address: usize, + include_dirs: []const []const u8, + file_entries: *ArrayList(FileEntry), -pub const ATE_void = 0x0; -pub const ATE_address = 0x1; -pub const ATE_boolean = 0x2; -pub const ATE_complex_float = 0x3; -pub const ATE_float = 0x4; -pub const ATE_signed = 0x5; -pub const ATE_signed_char = 0x6; -pub const ATE_unsigned = 0x7; -pub const ATE_unsigned_char = 0x8; + prev_address: usize, + prev_file: usize, + prev_line: i64, + prev_column: u64, + prev_is_stmt: bool, + prev_basic_block: bool, + prev_end_sequence: bool, -// DWARF 3. -pub const ATE_imaginary_float = 0x9; -pub const ATE_packed_decimal = 0xa; -pub const ATE_numeric_string = 0xb; -pub const ATE_edited = 0xc; -pub const ATE_signed_fixed = 0xd; -pub const ATE_unsigned_fixed = 0xe; -pub const ATE_decimal_float = 0xf; + // Reset the state machine following the DWARF specification + pub fn reset(self: *LineNumberProgram) void { + self.address = 0; + self.file = 1; + self.line = 1; + self.column = 0; + self.is_stmt = self.default_is_stmt; + self.basic_block = false; + self.end_sequence = false; + // Invalidate all the remaining fields + self.prev_address = 0; + self.prev_file = undefined; + self.prev_line = undefined; + self.prev_column = undefined; + self.prev_is_stmt = undefined; + self.prev_basic_block = undefined; + self.prev_end_sequence = undefined; + } -// DWARF 4. -pub const ATE_UTF = 0x10; + pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram { + return LineNumberProgram{ + .address = 0, + .file = 1, + .line = 1, + .column = 0, + .is_stmt = is_stmt, + .basic_block = false, + .end_sequence = false, + .include_dirs = include_dirs, + .file_entries = file_entries, + .default_is_stmt = is_stmt, + .target_address = target_address, + .prev_address = 0, + .prev_file = undefined, + .prev_line = undefined, + .prev_column = undefined, + .prev_is_stmt = undefined, + .prev_basic_block = undefined, + .prev_end_sequence = undefined, + }; + } -pub const ATE_lo_user = 0x80; -pub const ATE_hi_user = 0xff; + pub fn checkLineMatch(self: *LineNumberProgram) !?debug.LineInfo { + if (self.target_address >= self.prev_address and self.target_address < self.address) { + const file_entry = if (self.prev_file == 0) { + return error.MissingDebugInfo; + } else if (self.prev_file - 1 >= self.file_entries.len) { + return error.InvalidDebugInfo; + } else + &self.file_entries.items[self.prev_file - 1]; -// HP extensions. -pub const ATE_HP_float80 = 0x80; // Floating-point (80 bit). -pub const ATE_HP_complex_float80 = 0x81; // Complex floating-point (80 bit). -pub const ATE_HP_float128 = 0x82; // Floating-point (128 bit). -pub const ATE_HP_complex_float128 = 0x83; // Complex fp (128 bit). -pub const ATE_HP_floathpintel = 0x84; // Floating-point (82 bit IA64). -pub const ATE_HP_imaginary_float80 = 0x85; -pub const ATE_HP_imaginary_float128 = 0x86; -pub const ATE_HP_VAX_float = 0x88; // F or G floating. -pub const ATE_HP_VAX_float_d = 0x89; // D floating. -pub const ATE_HP_packed_decimal = 0x8a; // Cobol. -pub const ATE_HP_zoned_decimal = 0x8b; // Cobol. -pub const ATE_HP_edited = 0x8c; // Cobol. -pub const ATE_HP_signed_fixed = 0x8d; // Cobol. -pub const ATE_HP_unsigned_fixed = 0x8e; // Cobol. -pub const ATE_HP_VAX_complex_float = 0x8f; // F or G floating complex. -pub const ATE_HP_VAX_complex_float_d = 0x90; // D floating complex. + const dir_name = if (file_entry.dir_index >= self.include_dirs.len) { + return error.InvalidDebugInfo; + } else + self.include_dirs[file_entry.dir_index]; + const file_name = try fs.path.join(self.file_entries.allocator, &[_][]const u8{ dir_name, file_entry.file_name }); + errdefer self.file_entries.allocator.free(file_name); + return debug.LineInfo{ + .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0, + .column = self.prev_column, + .file_name = file_name, + .allocator = self.file_entries.allocator, + }; + } -pub const CFA_advance_loc = 0x40; -pub const CFA_offset = 0x80; -pub const CFA_restore = 0xc0; -pub const CFA_nop = 0x00; -pub const CFA_set_loc = 0x01; -pub const CFA_advance_loc1 = 0x02; -pub const CFA_advance_loc2 = 0x03; -pub const CFA_advance_loc4 = 0x04; -pub const CFA_offset_extended = 0x05; -pub const CFA_restore_extended = 0x06; -pub const CFA_undefined = 0x07; -pub const CFA_same_value = 0x08; -pub const CFA_register = 0x09; -pub const CFA_remember_state = 0x0a; -pub const CFA_restore_state = 0x0b; -pub const CFA_def_cfa = 0x0c; -pub const CFA_def_cfa_register = 0x0d; -pub const CFA_def_cfa_offset = 0x0e; + self.prev_address = self.address; + self.prev_file = self.file; + self.prev_line = self.line; + self.prev_column = self.column; + self.prev_is_stmt = self.is_stmt; + self.prev_basic_block = self.basic_block; + self.prev_end_sequence = self.end_sequence; + return null; + } +}; -// DWARF 3. -pub const CFA_def_cfa_expression = 0x0f; -pub const CFA_expression = 0x10; -pub const CFA_offset_extended_sf = 0x11; -pub const CFA_def_cfa_sf = 0x12; -pub const CFA_def_cfa_offset_sf = 0x13; -pub const CFA_val_offset = 0x14; -pub const CFA_val_offset_sf = 0x15; -pub const CFA_val_expression = 0x16; +fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 { + const first_32_bits = try in_stream.readIntLittle(u32); + is_64.* = (first_32_bits == 0xffffffff); + if (is_64.*) { + return in_stream.readIntLittle(u64); + } else { + if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; + // TODO this cast should not be needed + return @as(u64, first_32_bits); + } +} -pub const CFA_lo_user = 0x1c; -pub const CFA_hi_user = 0x3f; +// TODO the noasyncs here are workarounds +fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { + const buf = try allocator.alloc(u8, size); + errdefer allocator.free(buf); + if ((try noasync in_stream.read(buf)) < size) return error.EndOfFile; + return buf; +} -// SGI/MIPS specific. -pub const CFA_MIPS_advance_loc8 = 0x1d; +fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { + const buf = try readAllocBytes(allocator, in_stream, size); + return FormValue{ .Block = buf }; +} -// GNU extensions. -pub const CFA_GNU_window_save = 0x2d; -pub const CFA_GNU_args_size = 0x2e; -pub const CFA_GNU_negative_offset_extended = 0x2f; +// TODO the noasyncs here are workarounds +fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { + const block_len = try noasync in_stream.readVarInt(usize, builtin.Endian.Little, size); + return parseFormValueBlockLen(allocator, in_stream, block_len); +} -pub const CHILDREN_no = 0x00; -pub const CHILDREN_yes = 0x01; +fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, comptime size: i32) !FormValue { + // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here. + // `noasync` should be removed from all the function calls once it is fixed. + return FormValue{ + .Const = Constant{ + .signed = signed, + .payload = switch (size) { + 1 => try noasync in_stream.readIntLittle(u8), + 2 => try noasync in_stream.readIntLittle(u16), + 4 => try noasync in_stream.readIntLittle(u32), + 8 => try noasync in_stream.readIntLittle(u64), + -1 => blk: { + if (signed) { + const x = try noasync leb.readILEB128(i64, in_stream); + break :blk @bitCast(u64, x); + } else { + const x = try noasync leb.readULEB128(u64, in_stream); + break :blk x; + } + }, + else => @compileError("Invalid size"), + }, + }, + }; +} -pub const LNS_extended_op = 0x00; -pub const LNS_copy = 0x01; -pub const LNS_advance_pc = 0x02; -pub const LNS_advance_line = 0x03; -pub const LNS_set_file = 0x04; -pub const LNS_set_column = 0x05; -pub const LNS_negate_stmt = 0x06; -pub const LNS_set_basic_block = 0x07; -pub const LNS_const_add_pc = 0x08; -pub const LNS_fixed_advance_pc = 0x09; -pub const LNS_set_prologue_end = 0x0a; -pub const LNS_set_epilogue_begin = 0x0b; -pub const LNS_set_isa = 0x0c; +// TODO the noasyncs here are workarounds +fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 { + return if (is_64) try noasync in_stream.readIntLittle(u64) else @as(u64, try noasync in_stream.readIntLittle(u32)); +} -pub const LNE_end_sequence = 0x01; -pub const LNE_set_address = 0x02; -pub const LNE_define_file = 0x03; -pub const LNE_set_discriminator = 0x04; -pub const LNE_lo_user = 0x80; -pub const LNE_hi_user = 0xff; +// TODO the noasyncs here are workarounds +fn parseFormValueTargetAddrSize(in_stream: var) !u64 { + if (@sizeOf(usize) == 4) { + // TODO this cast should not be needed + return @as(u64, try noasync in_stream.readIntLittle(u32)); + } else if (@sizeOf(usize) == 8) { + return noasync in_stream.readIntLittle(u64); + } else { + unreachable; + } +} -pub const LANG_C89 = 0x0001; -pub const LANG_C = 0x0002; -pub const LANG_Ada83 = 0x0003; -pub const LANG_C_plus_plus = 0x0004; -pub const LANG_Cobol74 = 0x0005; -pub const LANG_Cobol85 = 0x0006; -pub const LANG_Fortran77 = 0x0007; -pub const LANG_Fortran90 = 0x0008; -pub const LANG_Pascal83 = 0x0009; -pub const LANG_Modula2 = 0x000a; -pub const LANG_Java = 0x000b; -pub const LANG_C99 = 0x000c; -pub const LANG_Ada95 = 0x000d; -pub const LANG_Fortran95 = 0x000e; -pub const LANG_PLI = 0x000f; -pub const LANG_ObjC = 0x0010; -pub const LANG_ObjC_plus_plus = 0x0011; -pub const LANG_UPC = 0x0012; -pub const LANG_D = 0x0013; -pub const LANG_Python = 0x0014; -pub const LANG_Go = 0x0016; -pub const LANG_C_plus_plus_11 = 0x001a; -pub const LANG_Rust = 0x001c; -pub const LANG_C11 = 0x001d; -pub const LANG_C_plus_plus_14 = 0x0021; -pub const LANG_Fortran03 = 0x0022; -pub const LANG_Fortran08 = 0x0023; -pub const LANG_lo_user = 0x8000; -pub const LANG_hi_user = 0xffff; -pub const LANG_Mips_Assembler = 0x8001; -pub const LANG_Upc = 0x8765; -pub const LANG_HP_Bliss = 0x8003; -pub const LANG_HP_Basic91 = 0x8004; -pub const LANG_HP_Pascal91 = 0x8005; -pub const LANG_HP_IMacro = 0x8006; -pub const LANG_HP_Assembler = 0x8007; +// TODO the noasyncs here are workarounds +fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, size: i32) !FormValue { + return FormValue{ + .Ref = switch (size) { + 1 => try noasync in_stream.readIntLittle(u8), + 2 => try noasync in_stream.readIntLittle(u16), + 4 => try noasync in_stream.readIntLittle(u32), + 8 => try noasync in_stream.readIntLittle(u64), + -1 => try noasync leb.readULEB128(u64, in_stream), + else => unreachable, + }, + }; +} + +// TODO the noasyncs here are workarounds +fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue { + return switch (form_id) { + FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) }, + FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), + FORM_block2 => parseFormValueBlock(allocator, in_stream, 2), + FORM_block4 => parseFormValueBlock(allocator, in_stream, 4), + FORM_block => x: { + const block_len = try noasync leb.readULEB128(usize, in_stream); + return parseFormValueBlockLen(allocator, in_stream, block_len); + }, + FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1), + FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2), + FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4), + FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8), + FORM_udata, FORM_sdata => { + const signed = form_id == FORM_sdata; + return parseFormValueConstant(allocator, in_stream, signed, -1); + }, + FORM_exprloc => { + const size = try noasync leb.readULEB128(usize, in_stream); + const buf = try readAllocBytes(allocator, in_stream, size); + return FormValue{ .ExprLoc = buf }; + }, + FORM_flag => FormValue{ .Flag = (try noasync in_stream.readByte()) != 0 }, + FORM_flag_present => FormValue{ .Flag = true }, + FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, + + FORM_ref1 => parseFormValueRef(allocator, in_stream, 1), + FORM_ref2 => parseFormValueRef(allocator, in_stream, 2), + FORM_ref4 => parseFormValueRef(allocator, in_stream, 4), + FORM_ref8 => parseFormValueRef(allocator, in_stream, 8), + FORM_ref_udata => parseFormValueRef(allocator, in_stream, -1), + + FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, + FORM_ref_sig8 => FormValue{ .Ref = try noasync in_stream.readIntLittle(u64) }, + + FORM_string => FormValue{ .String = try in_stream.readUntilDelimiterAlloc(allocator, 0, math.maxInt(usize)) }, + FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, + FORM_indirect => { + const child_form_id = try noasync leb.readULEB128(u64, in_stream); + const F = @TypeOf(async parseFormValue(allocator, in_stream, child_form_id, is_64)); + var frame = try allocator.create(F); + defer allocator.destroy(frame); + return await @asyncCall(frame, {}, parseFormValue, allocator, in_stream, child_form_id, is_64); + }, + else => error.InvalidDebugInfo, + }; +} + +fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { + for (abbrev_table.toSliceConst()) |*table_entry| { + if (table_entry.abbrev_code == abbrev_code) return table_entry; + } + return null; +} + +pub const DwarfInfo = struct { + endian: builtin.Endian, + // No memory is owned by the DwarfInfo + debug_info: []u8, + debug_abbrev: []u8, + debug_str: []u8, + debug_line: []u8, + debug_ranges: ?[]u8, + // Filled later by the initializer + abbrev_table_list: ArrayList(AbbrevTableHeader) = undefined, + compile_unit_list: ArrayList(CompileUnit) = undefined, + func_list: ArrayList(Func) = undefined, + + pub fn allocator(self: DwarfInfo) *mem.Allocator { + return self.abbrev_table_list.allocator; + } + + fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 { + for (di.func_list.toSliceConst()) |*func| { + if (func.pc_range) |range| { + if (address >= range.start and address < range.end) { + return func.name; + } + } + } + + return null; + } + + fn scanAllFunctions(di: *DwarfInfo) !void { + var s = io.SliceSeekableInStream.init(di.debug_info); + var this_unit_offset: u64 = 0; + + while (true) { + s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) { + error.EndOfStream => return, + else => return err, + }; + + var is_64: bool = undefined; + const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64); + if (unit_length == 0) return; + const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); + + const version = try s.stream.readInt(u16, di.endian); + if (version < 2 or version > 5) return error.InvalidDebugInfo; + + const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian); + + const address_size = try s.stream.readByte(); + if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; + + const compile_unit_pos = try s.seekable_stream.getPos(); + const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); + + try s.seekable_stream.seekTo(compile_unit_pos); + + const next_unit_pos = this_unit_offset + next_offset; + + while ((try s.seekable_stream.getPos()) < next_unit_pos) { + const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue; + const after_die_offset = try s.seekable_stream.getPos(); + + switch (die_obj.tag_id) { + TAG_subprogram, TAG_inlined_subroutine, TAG_subroutine, TAG_entry_point => { + const fn_name = x: { + var depth: i32 = 3; + var this_die_obj = die_obj; + // Prenvent endless loops + while (depth > 0) : (depth -= 1) { + if (this_die_obj.getAttr(AT_name)) |_| { + const name = try this_die_obj.getAttrString(di, AT_name); + break :x name; + } else if (this_die_obj.getAttr(AT_abstract_origin)) |ref| { + // Follow the DIE it points to and repeat + const ref_offset = try this_die_obj.getAttrRef(AT_abstract_origin); + if (ref_offset > next_offset) return error.InvalidDebugInfo; + try s.seekable_stream.seekTo(this_unit_offset + ref_offset); + this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; + } else if (this_die_obj.getAttr(AT_specification)) |ref| { + // Follow the DIE it points to and repeat + const ref_offset = try this_die_obj.getAttrRef(AT_specification); + if (ref_offset > next_offset) return error.InvalidDebugInfo; + try s.seekable_stream.seekTo(this_unit_offset + ref_offset); + this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; + } else { + break :x null; + } + } + + break :x null; + }; + + const pc_range = x: { + if (die_obj.getAttrAddr(AT_low_pc)) |low_pc| { + if (die_obj.getAttr(AT_high_pc)) |high_pc_value| { + const pc_end = switch (high_pc_value.*) { + FormValue.Address => |value| value, + FormValue.Const => |value| b: { + const offset = try value.asUnsignedLe(); + break :b (low_pc + offset); + }, + else => return error.InvalidDebugInfo, + }; + break :x PcRange{ + .start = low_pc, + .end = pc_end, + }; + } else { + break :x null; + } + } else |err| { + if (err != error.MissingDebugInfo) return err; + break :x null; + } + }; + + try di.func_list.append(Func{ + .name = fn_name, + .pc_range = pc_range, + }); + }, + else => {}, + } + + try s.seekable_stream.seekTo(after_die_offset); + } + + this_unit_offset += next_offset; + } + } + + fn scanAllCompileUnits(di: *DwarfInfo) !void { + var s = io.SliceSeekableInStream.init(di.debug_info); + var this_unit_offset: u64 = 0; + + while (true) { + s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) { + error.EndOfStream => return, + else => return err, + }; + + var is_64: bool = undefined; + const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64); + if (unit_length == 0) return; + const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); + + const version = try s.stream.readInt(u16, di.endian); + if (version < 2 or version > 5) return error.InvalidDebugInfo; + + const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian); + + const address_size = try s.stream.readByte(); + if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; + + const compile_unit_pos = try s.seekable_stream.getPos(); + const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); + + try s.seekable_stream.seekTo(compile_unit_pos); + + const compile_unit_die = try di.allocator().create(Die); + compile_unit_die.* = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; + + if (compile_unit_die.tag_id != TAG_compile_unit) return error.InvalidDebugInfo; + + const pc_range = x: { + if (compile_unit_die.getAttrAddr(AT_low_pc)) |low_pc| { + if (compile_unit_die.getAttr(AT_high_pc)) |high_pc_value| { + const pc_end = switch (high_pc_value.*) { + FormValue.Address => |value| value, + FormValue.Const => |value| b: { + const offset = try value.asUnsignedLe(); + break :b (low_pc + offset); + }, + else => return error.InvalidDebugInfo, + }; + break :x PcRange{ + .start = low_pc, + .end = pc_end, + }; + } else { + break :x null; + } + } else |err| { + if (err != error.MissingDebugInfo) return err; + break :x null; + } + }; + + try di.compile_unit_list.append(CompileUnit{ + .version = version, + .is_64 = is_64, + .pc_range = pc_range, + .die = compile_unit_die, + }); + + this_unit_offset += next_offset; + } + } + + fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { + for (di.compile_unit_list.toSlice()) |*compile_unit| { + if (compile_unit.pc_range) |range| { + if (target_address >= range.start and target_address < range.end) return compile_unit; + } + if (di.debug_ranges) |debug_ranges| { + if (compile_unit.die.getAttrSecOffset(AT_ranges)) |ranges_offset| { + var s = io.SliceSeekableInStream.init(debug_ranges); + + // All the addresses in the list are relative to the value + // specified by DW_AT_low_pc or to some other value encoded + // in the list itself. + // If no starting value is specified use zero. + var base_address = compile_unit.die.getAttrAddr(AT_low_pc) catch |err| switch (err) { + error.MissingDebugInfo => 0, + else => return err, + }; + + try s.seekable_stream.seekTo(ranges_offset); + + while (true) { + const begin_addr = try s.stream.readIntLittle(usize); + const end_addr = try s.stream.readIntLittle(usize); + if (begin_addr == 0 and end_addr == 0) { + break; + } + // This entry selects a new value for the base address + if (begin_addr == math.maxInt(usize)) { + base_address = end_addr; + continue; + } + if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) { + return compile_unit; + } + } + } else |err| { + if (err != error.MissingDebugInfo) return err; + continue; + } + } + } + return error.MissingDebugInfo; + } + + /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, + /// seeks in the stream and parses it. + fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { + for (di.abbrev_table_list.toSlice()) |*header| { + if (header.offset == abbrev_offset) { + return &header.table; + } + } + try di.abbrev_table_list.append(AbbrevTableHeader{ + .offset = abbrev_offset, + .table = try di.parseAbbrevTable(abbrev_offset), + }); + return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; + } + + fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable { + var s = io.SliceSeekableInStream.init(di.debug_abbrev); + + try s.seekable_stream.seekTo(offset); + var result = AbbrevTable.init(di.allocator()); + errdefer result.deinit(); + while (true) { + const abbrev_code = try leb.readULEB128(u64, &s.stream); + if (abbrev_code == 0) return result; + try result.append(AbbrevTableEntry{ + .abbrev_code = abbrev_code, + .tag_id = try leb.readULEB128(u64, &s.stream), + .has_children = (try s.stream.readByte()) == CHILDREN_yes, + .attrs = ArrayList(AbbrevAttr).init(di.allocator()), + }); + const attrs = &result.items[result.len - 1].attrs; + + while (true) { + const attr_id = try leb.readULEB128(u64, &s.stream); + const form_id = try leb.readULEB128(u64, &s.stream); + if (attr_id == 0 and form_id == 0) break; + try attrs.append(AbbrevAttr{ + .attr_id = attr_id, + .form_id = form_id, + }); + } + } + } + + fn parseDie(di: *DwarfInfo, in_stream: var, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { + const abbrev_code = try leb.readULEB128(u64, in_stream); + if (abbrev_code == 0) return null; + const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; + + var result = Die{ + .tag_id = table_entry.tag_id, + .has_children = table_entry.has_children, + .attrs = ArrayList(Die.Attr).init(di.allocator()), + }; + try result.attrs.resize(table_entry.attrs.len); + for (table_entry.attrs.toSliceConst()) |attr, i| { + result.attrs.items[i] = Die.Attr{ + .id = attr.attr_id, + .value = try parseFormValue(di.allocator(), in_stream, attr.form_id, is_64), + }; + } + return result; + } + + fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !debug.LineInfo { + var s = io.SliceSeekableInStream.init(di.debug_line); + + const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT_comp_dir); + const line_info_offset = try compile_unit.die.getAttrSecOffset(AT_stmt_list); + + try s.seekable_stream.seekTo(line_info_offset); + + var is_64: bool = undefined; + const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64); + if (unit_length == 0) { + return error.MissingDebugInfo; + } + const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); + + const version = try s.stream.readInt(u16, di.endian); + // TODO support 3 and 5 + if (version != 2 and version != 4) return error.InvalidDebugInfo; + + const prologue_length = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian); + const prog_start_offset = (try s.seekable_stream.getPos()) + prologue_length; + + const minimum_instruction_length = try s.stream.readByte(); + if (minimum_instruction_length == 0) return error.InvalidDebugInfo; + + if (version >= 4) { + // maximum_operations_per_instruction + _ = try s.stream.readByte(); + } + + const default_is_stmt = (try s.stream.readByte()) != 0; + const line_base = try s.stream.readByteSigned(); + + const line_range = try s.stream.readByte(); + if (line_range == 0) return error.InvalidDebugInfo; + + const opcode_base = try s.stream.readByte(); + + const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); + defer di.allocator().free(standard_opcode_lengths); + + { + var i: usize = 0; + while (i < opcode_base - 1) : (i += 1) { + standard_opcode_lengths[i] = try s.stream.readByte(); + } + } + + var include_directories = ArrayList([]u8).init(di.allocator()); + try include_directories.append(compile_unit_cwd); + while (true) { + const dir = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize)); + if (dir.len == 0) break; + try include_directories.append(dir); + } + + var file_entries = ArrayList(FileEntry).init(di.allocator()); + var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); + + while (true) { + const file_name = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize)); + if (file_name.len == 0) break; + const dir_index = try leb.readULEB128(usize, &s.stream); + const mtime = try leb.readULEB128(usize, &s.stream); + const len_bytes = try leb.readULEB128(usize, &s.stream); + try file_entries.append(FileEntry{ + .file_name = file_name, + .dir_index = dir_index, + .mtime = mtime, + .len_bytes = len_bytes, + }); + } + + try s.seekable_stream.seekTo(prog_start_offset); + + const next_unit_pos = line_info_offset + next_offset; + + while ((try s.seekable_stream.getPos()) < next_unit_pos) { + const opcode = try s.stream.readByte(); + + if (opcode == LNS_extended_op) { + const op_size = try leb.readULEB128(u64, &s.stream); + if (op_size < 1) return error.InvalidDebugInfo; + var sub_op = try s.stream.readByte(); + switch (sub_op) { + LNE_end_sequence => { + prog.end_sequence = true; + if (try prog.checkLineMatch()) |info| return info; + prog.reset(); + }, + LNE_set_address => { + const addr = try s.stream.readInt(usize, di.endian); + prog.address = addr; + }, + LNE_define_file => { + const file_name = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize)); + const dir_index = try leb.readULEB128(usize, &s.stream); + const mtime = try leb.readULEB128(usize, &s.stream); + const len_bytes = try leb.readULEB128(usize, &s.stream); + try file_entries.append(FileEntry{ + .file_name = file_name, + .dir_index = dir_index, + .mtime = mtime, + .len_bytes = len_bytes, + }); + }, + else => { + const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo; + try s.seekable_stream.seekBy(fwd_amt); + }, + } + } else if (opcode >= opcode_base) { + // special opcodes + const adjusted_opcode = opcode - opcode_base; + const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); + const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range); + prog.line += inc_line; + prog.address += inc_addr; + if (try prog.checkLineMatch()) |info| return info; + prog.basic_block = false; + } else { + switch (opcode) { + LNS_copy => { + if (try prog.checkLineMatch()) |info| return info; + prog.basic_block = false; + }, + LNS_advance_pc => { + const arg = try leb.readULEB128(usize, &s.stream); + prog.address += arg * minimum_instruction_length; + }, + LNS_advance_line => { + const arg = try leb.readILEB128(i64, &s.stream); + prog.line += arg; + }, + LNS_set_file => { + const arg = try leb.readULEB128(usize, &s.stream); + prog.file = arg; + }, + LNS_set_column => { + const arg = try leb.readULEB128(u64, &s.stream); + prog.column = arg; + }, + LNS_negate_stmt => { + prog.is_stmt = !prog.is_stmt; + }, + LNS_set_basic_block => { + prog.basic_block = true; + }, + LNS_const_add_pc => { + const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); + prog.address += inc_addr; + }, + LNS_fixed_advance_pc => { + const arg = try s.stream.readInt(u16, di.endian); + prog.address += arg; + }, + LNS_set_prologue_end => {}, + else => { + if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; + const len_bytes = standard_opcode_lengths[opcode - 1]; + try s.seekable_stream.seekBy(len_bytes); + }, + } + } + } + + return error.MissingDebugInfo; + } + + fn getString(di: *DwarfInfo, offset: u64) ![]u8 { + if (offset > di.debug_str.len) + return error.InvalidDebugInfo; + const casted_offset = math.cast(usize, offset) catch + return error.InvalidDebugInfo; + + // Valid strings always have a terminating zero byte + if (mem.indexOfScalarPos(u8, di.debug_str, casted_offset, 0)) |last| { + return di.debug_str[casted_offset..last]; + } + + return error.InvalidDebugInfo; + } +}; + +/// Initialize DWARF info. The caller has the responsibility to initialize most +/// the DwarfInfo fields before calling. These fields can be left undefined: +/// * abbrev_table_list +/// * compile_unit_list +pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { + di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); + di.compile_unit_list = ArrayList(CompileUnit).init(allocator); + di.func_list = ArrayList(Func).init(allocator); + try di.scanAllFunctions(); + try di.scanAllCompileUnits(); +} diff --git a/lib/std/dwarf_bits.zig b/lib/std/dwarf_bits.zig new file mode 100644 index 0000000000..2f3b29302d --- /dev/null +++ b/lib/std/dwarf_bits.zig @@ -0,0 +1,682 @@ +pub const TAG_padding = 0x00; +pub const TAG_array_type = 0x01; +pub const TAG_class_type = 0x02; +pub const TAG_entry_point = 0x03; +pub const TAG_enumeration_type = 0x04; +pub const TAG_formal_parameter = 0x05; +pub const TAG_imported_declaration = 0x08; +pub const TAG_label = 0x0a; +pub const TAG_lexical_block = 0x0b; +pub const TAG_member = 0x0d; +pub const TAG_pointer_type = 0x0f; +pub const TAG_reference_type = 0x10; +pub const TAG_compile_unit = 0x11; +pub const TAG_string_type = 0x12; +pub const TAG_structure_type = 0x13; +pub const TAG_subroutine = 0x14; +pub const TAG_subroutine_type = 0x15; +pub const TAG_typedef = 0x16; +pub const TAG_union_type = 0x17; +pub const TAG_unspecified_parameters = 0x18; +pub const TAG_variant = 0x19; +pub const TAG_common_block = 0x1a; +pub const TAG_common_inclusion = 0x1b; +pub const TAG_inheritance = 0x1c; +pub const TAG_inlined_subroutine = 0x1d; +pub const TAG_module = 0x1e; +pub const TAG_ptr_to_member_type = 0x1f; +pub const TAG_set_type = 0x20; +pub const TAG_subrange_type = 0x21; +pub const TAG_with_stmt = 0x22; +pub const TAG_access_declaration = 0x23; +pub const TAG_base_type = 0x24; +pub const TAG_catch_block = 0x25; +pub const TAG_const_type = 0x26; +pub const TAG_constant = 0x27; +pub const TAG_enumerator = 0x28; +pub const TAG_file_type = 0x29; +pub const TAG_friend = 0x2a; +pub const TAG_namelist = 0x2b; +pub const TAG_namelist_item = 0x2c; +pub const TAG_packed_type = 0x2d; +pub const TAG_subprogram = 0x2e; +pub const TAG_template_type_param = 0x2f; +pub const TAG_template_value_param = 0x30; +pub const TAG_thrown_type = 0x31; +pub const TAG_try_block = 0x32; +pub const TAG_variant_part = 0x33; +pub const TAG_variable = 0x34; +pub const TAG_volatile_type = 0x35; + +// DWARF 3 +pub const TAG_dwarf_procedure = 0x36; +pub const TAG_restrict_type = 0x37; +pub const TAG_interface_type = 0x38; +pub const TAG_namespace = 0x39; +pub const TAG_imported_module = 0x3a; +pub const TAG_unspecified_type = 0x3b; +pub const TAG_partial_unit = 0x3c; +pub const TAG_imported_unit = 0x3d; +pub const TAG_condition = 0x3f; +pub const TAG_shared_type = 0x40; + +// DWARF 4 +pub const TAG_type_unit = 0x41; +pub const TAG_rvalue_reference_type = 0x42; +pub const TAG_template_alias = 0x43; + +pub const TAG_lo_user = 0x4080; +pub const TAG_hi_user = 0xffff; + +// SGI/MIPS Extensions. +pub const DW_TAG_MIPS_loop = 0x4081; + +// HP extensions. See: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz . +pub const TAG_HP_array_descriptor = 0x4090; +pub const TAG_HP_Bliss_field = 0x4091; +pub const TAG_HP_Bliss_field_set = 0x4092; + +// GNU extensions. +pub const TAG_format_label = 0x4101; // For FORTRAN 77 and Fortran 90. +pub const TAG_function_template = 0x4102; // For C++. +pub const TAG_class_template = 0x4103; //For C++. +pub const TAG_GNU_BINCL = 0x4104; +pub const TAG_GNU_EINCL = 0x4105; + +// Template template parameter. +// See http://gcc.gnu.org/wiki/TemplateParmsDwarf . +pub const TAG_GNU_template_template_param = 0x4106; + +// Template parameter pack extension = specified at +// http://wiki.dwarfstd.org/index.php?title=C%2B%2B0x:_Variadic_templates +// The values of these two TAGS are in the DW_TAG_GNU_* space until the tags +// are properly part of DWARF 5. +pub const TAG_GNU_template_parameter_pack = 0x4107; +pub const TAG_GNU_formal_parameter_pack = 0x4108; +// The GNU call site extension = specified at +// http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open . +// The values of these two TAGS are in the DW_TAG_GNU_* space until the tags +// are properly part of DWARF 5. +pub const TAG_GNU_call_site = 0x4109; +pub const TAG_GNU_call_site_parameter = 0x410a; +// Extensions for UPC. See: http://dwarfstd.org/doc/DWARF4.pdf. +pub const TAG_upc_shared_type = 0x8765; +pub const TAG_upc_strict_type = 0x8766; +pub const TAG_upc_relaxed_type = 0x8767; +// PGI (STMicroelectronics; extensions. No documentation available. +pub const TAG_PGI_kanji_type = 0xA000; +pub const TAG_PGI_interface_block = 0xA020; + +pub const FORM_addr = 0x01; +pub const FORM_block2 = 0x03; +pub const FORM_block4 = 0x04; +pub const FORM_data2 = 0x05; +pub const FORM_data4 = 0x06; +pub const FORM_data8 = 0x07; +pub const FORM_string = 0x08; +pub const FORM_block = 0x09; +pub const FORM_block1 = 0x0a; +pub const FORM_data1 = 0x0b; +pub const FORM_flag = 0x0c; +pub const FORM_sdata = 0x0d; +pub const FORM_strp = 0x0e; +pub const FORM_udata = 0x0f; +pub const FORM_ref_addr = 0x10; +pub const FORM_ref1 = 0x11; +pub const FORM_ref2 = 0x12; +pub const FORM_ref4 = 0x13; +pub const FORM_ref8 = 0x14; +pub const FORM_ref_udata = 0x15; +pub const FORM_indirect = 0x16; +pub const FORM_sec_offset = 0x17; +pub const FORM_exprloc = 0x18; +pub const FORM_flag_present = 0x19; +pub const FORM_ref_sig8 = 0x20; + +// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission. +pub const FORM_GNU_addr_index = 0x1f01; +pub const FORM_GNU_str_index = 0x1f02; + +// Extensions for DWZ multifile. +// See http://www.dwarfstd.org/ShowIssue.php?issue=120604.1&type=open . +pub const FORM_GNU_ref_alt = 0x1f20; +pub const FORM_GNU_strp_alt = 0x1f21; + +pub const AT_sibling = 0x01; +pub const AT_location = 0x02; +pub const AT_name = 0x03; +pub const AT_ordering = 0x09; +pub const AT_subscr_data = 0x0a; +pub const AT_byte_size = 0x0b; +pub const AT_bit_offset = 0x0c; +pub const AT_bit_size = 0x0d; +pub const AT_element_list = 0x0f; +pub const AT_stmt_list = 0x10; +pub const AT_low_pc = 0x11; +pub const AT_high_pc = 0x12; +pub const AT_language = 0x13; +pub const AT_member = 0x14; +pub const AT_discr = 0x15; +pub const AT_discr_value = 0x16; +pub const AT_visibility = 0x17; +pub const AT_import = 0x18; +pub const AT_string_length = 0x19; +pub const AT_common_reference = 0x1a; +pub const AT_comp_dir = 0x1b; +pub const AT_const_value = 0x1c; +pub const AT_containing_type = 0x1d; +pub const AT_default_value = 0x1e; +pub const AT_inline = 0x20; +pub const AT_is_optional = 0x21; +pub const AT_lower_bound = 0x22; +pub const AT_producer = 0x25; +pub const AT_prototyped = 0x27; +pub const AT_return_addr = 0x2a; +pub const AT_start_scope = 0x2c; +pub const AT_bit_stride = 0x2e; +pub const AT_upper_bound = 0x2f; +pub const AT_abstract_origin = 0x31; +pub const AT_accessibility = 0x32; +pub const AT_address_class = 0x33; +pub const AT_artificial = 0x34; +pub const AT_base_types = 0x35; +pub const AT_calling_convention = 0x36; +pub const AT_count = 0x37; +pub const AT_data_member_location = 0x38; +pub const AT_decl_column = 0x39; +pub const AT_decl_file = 0x3a; +pub const AT_decl_line = 0x3b; +pub const AT_declaration = 0x3c; +pub const AT_discr_list = 0x3d; +pub const AT_encoding = 0x3e; +pub const AT_external = 0x3f; +pub const AT_frame_base = 0x40; +pub const AT_friend = 0x41; +pub const AT_identifier_case = 0x42; +pub const AT_macro_info = 0x43; +pub const AT_namelist_items = 0x44; +pub const AT_priority = 0x45; +pub const AT_segment = 0x46; +pub const AT_specification = 0x47; +pub const AT_static_link = 0x48; +pub const AT_type = 0x49; +pub const AT_use_location = 0x4a; +pub const AT_variable_parameter = 0x4b; +pub const AT_virtuality = 0x4c; +pub const AT_vtable_elem_location = 0x4d; + +// DWARF 3 values. +pub const AT_allocated = 0x4e; +pub const AT_associated = 0x4f; +pub const AT_data_location = 0x50; +pub const AT_byte_stride = 0x51; +pub const AT_entry_pc = 0x52; +pub const AT_use_UTF8 = 0x53; +pub const AT_extension = 0x54; +pub const AT_ranges = 0x55; +pub const AT_trampoline = 0x56; +pub const AT_call_column = 0x57; +pub const AT_call_file = 0x58; +pub const AT_call_line = 0x59; +pub const AT_description = 0x5a; +pub const AT_binary_scale = 0x5b; +pub const AT_decimal_scale = 0x5c; +pub const AT_small = 0x5d; +pub const AT_decimal_sign = 0x5e; +pub const AT_digit_count = 0x5f; +pub const AT_picture_string = 0x60; +pub const AT_mutable = 0x61; +pub const AT_threads_scaled = 0x62; +pub const AT_explicit = 0x63; +pub const AT_object_pointer = 0x64; +pub const AT_endianity = 0x65; +pub const AT_elemental = 0x66; +pub const AT_pure = 0x67; +pub const AT_recursive = 0x68; + +// DWARF 4. +pub const AT_signature = 0x69; +pub const AT_main_subprogram = 0x6a; +pub const AT_data_bit_offset = 0x6b; +pub const AT_const_expr = 0x6c; +pub const AT_enum_class = 0x6d; +pub const AT_linkage_name = 0x6e; + +// DWARF 5 +pub const AT_alignment = 0x88; + +pub const AT_lo_user = 0x2000; // Implementation-defined range start. +pub const AT_hi_user = 0x3fff; // Implementation-defined range end. + +// SGI/MIPS extensions. +pub const AT_MIPS_fde = 0x2001; +pub const AT_MIPS_loop_begin = 0x2002; +pub const AT_MIPS_tail_loop_begin = 0x2003; +pub const AT_MIPS_epilog_begin = 0x2004; +pub const AT_MIPS_loop_unroll_factor = 0x2005; +pub const AT_MIPS_software_pipeline_depth = 0x2006; +pub const AT_MIPS_linkage_name = 0x2007; +pub const AT_MIPS_stride = 0x2008; +pub const AT_MIPS_abstract_name = 0x2009; +pub const AT_MIPS_clone_origin = 0x200a; +pub const AT_MIPS_has_inlines = 0x200b; + +// HP extensions. +pub const AT_HP_block_index = 0x2000; +pub const AT_HP_unmodifiable = 0x2001; // Same as DW_AT_MIPS_fde. +pub const AT_HP_prologue = 0x2005; // Same as DW_AT_MIPS_loop_unroll. +pub const AT_HP_epilogue = 0x2008; // Same as DW_AT_MIPS_stride. +pub const AT_HP_actuals_stmt_list = 0x2010; +pub const AT_HP_proc_per_section = 0x2011; +pub const AT_HP_raw_data_ptr = 0x2012; +pub const AT_HP_pass_by_reference = 0x2013; +pub const AT_HP_opt_level = 0x2014; +pub const AT_HP_prof_version_id = 0x2015; +pub const AT_HP_opt_flags = 0x2016; +pub const AT_HP_cold_region_low_pc = 0x2017; +pub const AT_HP_cold_region_high_pc = 0x2018; +pub const AT_HP_all_variables_modifiable = 0x2019; +pub const AT_HP_linkage_name = 0x201a; +pub const AT_HP_prof_flags = 0x201b; // In comp unit of procs_info for -g. +pub const AT_HP_unit_name = 0x201f; +pub const AT_HP_unit_size = 0x2020; +pub const AT_HP_widened_byte_size = 0x2021; +pub const AT_HP_definition_points = 0x2022; +pub const AT_HP_default_location = 0x2023; +pub const AT_HP_is_result_param = 0x2029; + +// GNU extensions. +pub const AT_sf_names = 0x2101; +pub const AT_src_info = 0x2102; +pub const AT_mac_info = 0x2103; +pub const AT_src_coords = 0x2104; +pub const AT_body_begin = 0x2105; +pub const AT_body_end = 0x2106; +pub const AT_GNU_vector = 0x2107; +// Thread-safety annotations. +// See http://gcc.gnu.org/wiki/ThreadSafetyAnnotation . +pub const AT_GNU_guarded_by = 0x2108; +pub const AT_GNU_pt_guarded_by = 0x2109; +pub const AT_GNU_guarded = 0x210a; +pub const AT_GNU_pt_guarded = 0x210b; +pub const AT_GNU_locks_excluded = 0x210c; +pub const AT_GNU_exclusive_locks_required = 0x210d; +pub const AT_GNU_shared_locks_required = 0x210e; +// One-definition rule violation detection. +// See http://gcc.gnu.org/wiki/DwarfSeparateTypeInfo . +pub const AT_GNU_odr_signature = 0x210f; +// Template template argument name. +// See http://gcc.gnu.org/wiki/TemplateParmsDwarf . +pub const AT_GNU_template_name = 0x2110; +// The GNU call site extension. +// See http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open . +pub const AT_GNU_call_site_value = 0x2111; +pub const AT_GNU_call_site_data_value = 0x2112; +pub const AT_GNU_call_site_target = 0x2113; +pub const AT_GNU_call_site_target_clobbered = 0x2114; +pub const AT_GNU_tail_call = 0x2115; +pub const AT_GNU_all_tail_call_sites = 0x2116; +pub const AT_GNU_all_call_sites = 0x2117; +pub const AT_GNU_all_source_call_sites = 0x2118; +// Section offset into .debug_macro section. +pub const AT_GNU_macros = 0x2119; +// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission. +pub const AT_GNU_dwo_name = 0x2130; +pub const AT_GNU_dwo_id = 0x2131; +pub const AT_GNU_ranges_base = 0x2132; +pub const AT_GNU_addr_base = 0x2133; +pub const AT_GNU_pubnames = 0x2134; +pub const AT_GNU_pubtypes = 0x2135; +// VMS extensions. +pub const AT_VMS_rtnbeg_pd_address = 0x2201; +// GNAT extensions. +// GNAT descriptive type. +// See http://gcc.gnu.org/wiki/DW_AT_GNAT_descriptive_type . +pub const AT_use_GNAT_descriptive_type = 0x2301; +pub const AT_GNAT_descriptive_type = 0x2302; +// UPC extension. +pub const AT_upc_threads_scaled = 0x3210; +// PGI (STMicroelectronics) extensions. +pub const AT_PGI_lbase = 0x3a00; +pub const AT_PGI_soffset = 0x3a01; +pub const AT_PGI_lstride = 0x3a02; + +pub const OP_addr = 0x03; +pub const OP_deref = 0x06; +pub const OP_const1u = 0x08; +pub const OP_const1s = 0x09; +pub const OP_const2u = 0x0a; +pub const OP_const2s = 0x0b; +pub const OP_const4u = 0x0c; +pub const OP_const4s = 0x0d; +pub const OP_const8u = 0x0e; +pub const OP_const8s = 0x0f; +pub const OP_constu = 0x10; +pub const OP_consts = 0x11; +pub const OP_dup = 0x12; +pub const OP_drop = 0x13; +pub const OP_over = 0x14; +pub const OP_pick = 0x15; +pub const OP_swap = 0x16; +pub const OP_rot = 0x17; +pub const OP_xderef = 0x18; +pub const OP_abs = 0x19; +pub const OP_and = 0x1a; +pub const OP_div = 0x1b; +pub const OP_minus = 0x1c; +pub const OP_mod = 0x1d; +pub const OP_mul = 0x1e; +pub const OP_neg = 0x1f; +pub const OP_not = 0x20; +pub const OP_or = 0x21; +pub const OP_plus = 0x22; +pub const OP_plus_uconst = 0x23; +pub const OP_shl = 0x24; +pub const OP_shr = 0x25; +pub const OP_shra = 0x26; +pub const OP_xor = 0x27; +pub const OP_bra = 0x28; +pub const OP_eq = 0x29; +pub const OP_ge = 0x2a; +pub const OP_gt = 0x2b; +pub const OP_le = 0x2c; +pub const OP_lt = 0x2d; +pub const OP_ne = 0x2e; +pub const OP_skip = 0x2f; +pub const OP_lit0 = 0x30; +pub const OP_lit1 = 0x31; +pub const OP_lit2 = 0x32; +pub const OP_lit3 = 0x33; +pub const OP_lit4 = 0x34; +pub const OP_lit5 = 0x35; +pub const OP_lit6 = 0x36; +pub const OP_lit7 = 0x37; +pub const OP_lit8 = 0x38; +pub const OP_lit9 = 0x39; +pub const OP_lit10 = 0x3a; +pub const OP_lit11 = 0x3b; +pub const OP_lit12 = 0x3c; +pub const OP_lit13 = 0x3d; +pub const OP_lit14 = 0x3e; +pub const OP_lit15 = 0x3f; +pub const OP_lit16 = 0x40; +pub const OP_lit17 = 0x41; +pub const OP_lit18 = 0x42; +pub const OP_lit19 = 0x43; +pub const OP_lit20 = 0x44; +pub const OP_lit21 = 0x45; +pub const OP_lit22 = 0x46; +pub const OP_lit23 = 0x47; +pub const OP_lit24 = 0x48; +pub const OP_lit25 = 0x49; +pub const OP_lit26 = 0x4a; +pub const OP_lit27 = 0x4b; +pub const OP_lit28 = 0x4c; +pub const OP_lit29 = 0x4d; +pub const OP_lit30 = 0x4e; +pub const OP_lit31 = 0x4f; +pub const OP_reg0 = 0x50; +pub const OP_reg1 = 0x51; +pub const OP_reg2 = 0x52; +pub const OP_reg3 = 0x53; +pub const OP_reg4 = 0x54; +pub const OP_reg5 = 0x55; +pub const OP_reg6 = 0x56; +pub const OP_reg7 = 0x57; +pub const OP_reg8 = 0x58; +pub const OP_reg9 = 0x59; +pub const OP_reg10 = 0x5a; +pub const OP_reg11 = 0x5b; +pub const OP_reg12 = 0x5c; +pub const OP_reg13 = 0x5d; +pub const OP_reg14 = 0x5e; +pub const OP_reg15 = 0x5f; +pub const OP_reg16 = 0x60; +pub const OP_reg17 = 0x61; +pub const OP_reg18 = 0x62; +pub const OP_reg19 = 0x63; +pub const OP_reg20 = 0x64; +pub const OP_reg21 = 0x65; +pub const OP_reg22 = 0x66; +pub const OP_reg23 = 0x67; +pub const OP_reg24 = 0x68; +pub const OP_reg25 = 0x69; +pub const OP_reg26 = 0x6a; +pub const OP_reg27 = 0x6b; +pub const OP_reg28 = 0x6c; +pub const OP_reg29 = 0x6d; +pub const OP_reg30 = 0x6e; +pub const OP_reg31 = 0x6f; +pub const OP_breg0 = 0x70; +pub const OP_breg1 = 0x71; +pub const OP_breg2 = 0x72; +pub const OP_breg3 = 0x73; +pub const OP_breg4 = 0x74; +pub const OP_breg5 = 0x75; +pub const OP_breg6 = 0x76; +pub const OP_breg7 = 0x77; +pub const OP_breg8 = 0x78; +pub const OP_breg9 = 0x79; +pub const OP_breg10 = 0x7a; +pub const OP_breg11 = 0x7b; +pub const OP_breg12 = 0x7c; +pub const OP_breg13 = 0x7d; +pub const OP_breg14 = 0x7e; +pub const OP_breg15 = 0x7f; +pub const OP_breg16 = 0x80; +pub const OP_breg17 = 0x81; +pub const OP_breg18 = 0x82; +pub const OP_breg19 = 0x83; +pub const OP_breg20 = 0x84; +pub const OP_breg21 = 0x85; +pub const OP_breg22 = 0x86; +pub const OP_breg23 = 0x87; +pub const OP_breg24 = 0x88; +pub const OP_breg25 = 0x89; +pub const OP_breg26 = 0x8a; +pub const OP_breg27 = 0x8b; +pub const OP_breg28 = 0x8c; +pub const OP_breg29 = 0x8d; +pub const OP_breg30 = 0x8e; +pub const OP_breg31 = 0x8f; +pub const OP_regx = 0x90; +pub const OP_fbreg = 0x91; +pub const OP_bregx = 0x92; +pub const OP_piece = 0x93; +pub const OP_deref_size = 0x94; +pub const OP_xderef_size = 0x95; +pub const OP_nop = 0x96; + +// DWARF 3 extensions. +pub const OP_push_object_address = 0x97; +pub const OP_call2 = 0x98; +pub const OP_call4 = 0x99; +pub const OP_call_ref = 0x9a; +pub const OP_form_tls_address = 0x9b; +pub const OP_call_frame_cfa = 0x9c; +pub const OP_bit_piece = 0x9d; + +// DWARF 4 extensions. +pub const OP_implicit_value = 0x9e; +pub const OP_stack_value = 0x9f; + +pub const OP_lo_user = 0xe0; // Implementation-defined range start. +pub const OP_hi_user = 0xff; // Implementation-defined range end. + +// GNU extensions. +pub const OP_GNU_push_tls_address = 0xe0; +// The following is for marking variables that are uninitialized. +pub const OP_GNU_uninit = 0xf0; +pub const OP_GNU_encoded_addr = 0xf1; +// The GNU implicit pointer extension. +// See http://www.dwarfstd.org/ShowIssue.php?issue=100831.1&type=open . +pub const OP_GNU_implicit_pointer = 0xf2; +// The GNU entry value extension. +// See http://www.dwarfstd.org/ShowIssue.php?issue=100909.1&type=open . +pub const OP_GNU_entry_value = 0xf3; +// The GNU typed stack extension. +// See http://www.dwarfstd.org/doc/040408.1.html . +pub const OP_GNU_const_type = 0xf4; +pub const OP_GNU_regval_type = 0xf5; +pub const OP_GNU_deref_type = 0xf6; +pub const OP_GNU_convert = 0xf7; +pub const OP_GNU_reinterpret = 0xf9; +// The GNU parameter ref extension. +pub const OP_GNU_parameter_ref = 0xfa; +// Extension for Fission. See http://gcc.gnu.org/wiki/DebugFission. +pub const OP_GNU_addr_index = 0xfb; +pub const OP_GNU_const_index = 0xfc; +// HP extensions. +pub const OP_HP_unknown = 0xe0; // Ouch, the same as GNU_push_tls_address. +pub const OP_HP_is_value = 0xe1; +pub const OP_HP_fltconst4 = 0xe2; +pub const OP_HP_fltconst8 = 0xe3; +pub const OP_HP_mod_range = 0xe4; +pub const OP_HP_unmod_range = 0xe5; +pub const OP_HP_tls = 0xe6; +// PGI (STMicroelectronics) extensions. +pub const OP_PGI_omp_thread_num = 0xf8; + +pub const ATE_void = 0x0; +pub const ATE_address = 0x1; +pub const ATE_boolean = 0x2; +pub const ATE_complex_float = 0x3; +pub const ATE_float = 0x4; +pub const ATE_signed = 0x5; +pub const ATE_signed_char = 0x6; +pub const ATE_unsigned = 0x7; +pub const ATE_unsigned_char = 0x8; + +// DWARF 3. +pub const ATE_imaginary_float = 0x9; +pub const ATE_packed_decimal = 0xa; +pub const ATE_numeric_string = 0xb; +pub const ATE_edited = 0xc; +pub const ATE_signed_fixed = 0xd; +pub const ATE_unsigned_fixed = 0xe; +pub const ATE_decimal_float = 0xf; + +// DWARF 4. +pub const ATE_UTF = 0x10; + +pub const ATE_lo_user = 0x80; +pub const ATE_hi_user = 0xff; + +// HP extensions. +pub const ATE_HP_float80 = 0x80; // Floating-point (80 bit). +pub const ATE_HP_complex_float80 = 0x81; // Complex floating-point (80 bit). +pub const ATE_HP_float128 = 0x82; // Floating-point (128 bit). +pub const ATE_HP_complex_float128 = 0x83; // Complex fp (128 bit). +pub const ATE_HP_floathpintel = 0x84; // Floating-point (82 bit IA64). +pub const ATE_HP_imaginary_float80 = 0x85; +pub const ATE_HP_imaginary_float128 = 0x86; +pub const ATE_HP_VAX_float = 0x88; // F or G floating. +pub const ATE_HP_VAX_float_d = 0x89; // D floating. +pub const ATE_HP_packed_decimal = 0x8a; // Cobol. +pub const ATE_HP_zoned_decimal = 0x8b; // Cobol. +pub const ATE_HP_edited = 0x8c; // Cobol. +pub const ATE_HP_signed_fixed = 0x8d; // Cobol. +pub const ATE_HP_unsigned_fixed = 0x8e; // Cobol. +pub const ATE_HP_VAX_complex_float = 0x8f; // F or G floating complex. +pub const ATE_HP_VAX_complex_float_d = 0x90; // D floating complex. + +pub const CFA_advance_loc = 0x40; +pub const CFA_offset = 0x80; +pub const CFA_restore = 0xc0; +pub const CFA_nop = 0x00; +pub const CFA_set_loc = 0x01; +pub const CFA_advance_loc1 = 0x02; +pub const CFA_advance_loc2 = 0x03; +pub const CFA_advance_loc4 = 0x04; +pub const CFA_offset_extended = 0x05; +pub const CFA_restore_extended = 0x06; +pub const CFA_undefined = 0x07; +pub const CFA_same_value = 0x08; +pub const CFA_register = 0x09; +pub const CFA_remember_state = 0x0a; +pub const CFA_restore_state = 0x0b; +pub const CFA_def_cfa = 0x0c; +pub const CFA_def_cfa_register = 0x0d; +pub const CFA_def_cfa_offset = 0x0e; + +// DWARF 3. +pub const CFA_def_cfa_expression = 0x0f; +pub const CFA_expression = 0x10; +pub const CFA_offset_extended_sf = 0x11; +pub const CFA_def_cfa_sf = 0x12; +pub const CFA_def_cfa_offset_sf = 0x13; +pub const CFA_val_offset = 0x14; +pub const CFA_val_offset_sf = 0x15; +pub const CFA_val_expression = 0x16; + +pub const CFA_lo_user = 0x1c; +pub const CFA_hi_user = 0x3f; + +// SGI/MIPS specific. +pub const CFA_MIPS_advance_loc8 = 0x1d; + +// GNU extensions. +pub const CFA_GNU_window_save = 0x2d; +pub const CFA_GNU_args_size = 0x2e; +pub const CFA_GNU_negative_offset_extended = 0x2f; + +pub const CHILDREN_no = 0x00; +pub const CHILDREN_yes = 0x01; + +pub const LNS_extended_op = 0x00; +pub const LNS_copy = 0x01; +pub const LNS_advance_pc = 0x02; +pub const LNS_advance_line = 0x03; +pub const LNS_set_file = 0x04; +pub const LNS_set_column = 0x05; +pub const LNS_negate_stmt = 0x06; +pub const LNS_set_basic_block = 0x07; +pub const LNS_const_add_pc = 0x08; +pub const LNS_fixed_advance_pc = 0x09; +pub const LNS_set_prologue_end = 0x0a; +pub const LNS_set_epilogue_begin = 0x0b; +pub const LNS_set_isa = 0x0c; + +pub const LNE_end_sequence = 0x01; +pub const LNE_set_address = 0x02; +pub const LNE_define_file = 0x03; +pub const LNE_set_discriminator = 0x04; +pub const LNE_lo_user = 0x80; +pub const LNE_hi_user = 0xff; + +pub const LANG_C89 = 0x0001; +pub const LANG_C = 0x0002; +pub const LANG_Ada83 = 0x0003; +pub const LANG_C_plus_plus = 0x0004; +pub const LANG_Cobol74 = 0x0005; +pub const LANG_Cobol85 = 0x0006; +pub const LANG_Fortran77 = 0x0007; +pub const LANG_Fortran90 = 0x0008; +pub const LANG_Pascal83 = 0x0009; +pub const LANG_Modula2 = 0x000a; +pub const LANG_Java = 0x000b; +pub const LANG_C99 = 0x000c; +pub const LANG_Ada95 = 0x000d; +pub const LANG_Fortran95 = 0x000e; +pub const LANG_PLI = 0x000f; +pub const LANG_ObjC = 0x0010; +pub const LANG_ObjC_plus_plus = 0x0011; +pub const LANG_UPC = 0x0012; +pub const LANG_D = 0x0013; +pub const LANG_Python = 0x0014; +pub const LANG_Go = 0x0016; +pub const LANG_C_plus_plus_11 = 0x001a; +pub const LANG_Rust = 0x001c; +pub const LANG_C11 = 0x001d; +pub const LANG_C_plus_plus_14 = 0x0021; +pub const LANG_Fortran03 = 0x0022; +pub const LANG_Fortran08 = 0x0023; +pub const LANG_lo_user = 0x8000; +pub const LANG_hi_user = 0xffff; +pub const LANG_Mips_Assembler = 0x8001; +pub const LANG_Upc = 0x8765; +pub const LANG_HP_Bliss = 0x8003; +pub const LANG_HP_Basic91 = 0x8004; +pub const LANG_HP_Pascal91 = 0x8005; +pub const LANG_HP_IMacro = 0x8006; +pub const LANG_HP_Assembler = 0x8007; From b0b60cd46854f05c830b51693acd2f1ea7b1ee1a Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 9 Feb 2020 11:55:43 +0100 Subject: [PATCH 02/99] tmp --- lib/std/debug.zig | 157 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 139 insertions(+), 18 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 65b591199b..a36efe441b 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -726,32 +726,38 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt } pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { - const compile_unit = debug_info.findCompileUnit(address) catch { + // XXX Print as much as possible anyway + const module = try debug_info.lookupByAddress(address); + + const reloc_address = address - module.base_address; + warn("reloc {x} => {x}\n", .{ address, reloc_address }); + + if (module.dwarf.findCompileUnit(reloc_address) catch null) |compile_unit| { + const compile_unit_name = try compile_unit.die.getAttrString(&module.dwarf, DW.AT_name); + const symbol_name = module.dwarf.getSymbolName(reloc_address) orelse "???"; + const line_info = module.dwarf.getLineNumberInfo(compile_unit.*, reloc_address) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => null, + else => return err, + }; + defer if (line_info) |li| li.deinit(); + return printLineInfo( out_stream, - null, + line_info, address, - "???", - "???", + symbol_name, + compile_unit_name, tty_config, printLineFromFileAnyOs, ); - }; + } - const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); - const symbol_name = debug_info.getSymbolName(address) orelse "???"; - const line_info = debug_info.getLineNumberInfo(compile_unit.*, address) catch |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => null, - else => return err, - }; - defer if (line_info) |li| li.deinit(); - - try printLineInfo( + return printLineInfo( out_stream, - line_info, + null, address, - symbol_name, - compile_unit_name, + "???", + "???", tty_config, printLineFromFileAnyOs, ); @@ -824,6 +830,10 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { if (comptime std.Target.current.isDarwin()) { return noasync openSelfDebugInfoMacOs(allocator); } + if (builtin.os == .linux) { + _ = try allocator.create(u32); + return DebugInfo.init(allocator); + } return noasync openSelfDebugInfoPosix(allocator); } @@ -1190,7 +1200,113 @@ const MachoSymbol = struct { } }; -pub const DebugInfo = switch (builtin.os) { +pub const DebugInfo = struct { + allocator: *mem.Allocator, + address_map: std.StringHashMap(*ObjectDebugInfo), + + pub fn init(allocator: *mem.Allocator) DebugInfo { + return DebugInfo{ + .allocator = allocator, + .address_map = std.StringHashMap(*ObjectDebugInfo).init(allocator), + }; + } + + pub fn deinit(self: *DebugInfo) void { + self.address_map.deinit(); + } + + pub fn lookupByAddress(self: *DebugInfo, address: usize) !*ObjectDebugInfo { + const obj_di = try self.lookupModuleDl(address); + return obj_di; + } + + fn lookupModuleDl(self: *DebugInfo, address: usize) !*ObjectDebugInfo { + var ctx = DIPContext{ .address = address }; + + warn("lookup {x}\n", .{address}); + + // XXX Locking? + if (os.dl_iterate_phdr(DIPContext, dl_iterate_phdr_callback, &ctx) == 0) + return error.DebugInfoNotFound; + + warn("found in \"{s}\"\n", .{ctx.name}); + + if (self.address_map.getValue(ctx.name)) |obj_di| { + warn("cache hit!\n", .{}); + return obj_di; + } + + const exe_file = if (ctx.name.len > 0) + try fs.openFileAbsolute(ctx.name, .{}) + else + try fs.openSelfExe(); + defer exe_file.close(); + + const exe_len = math.cast(usize, try exe_file.getEndPos()) catch + return error.DebugInfoTooLarge; + const exe_mmap = try os.mmap( + null, + exe_len, + os.PROT_READ, + os.MAP_SHARED, + exe_file.handle, + 0, + ); + errdefer os.munmap(exe_mmap); + + const obj_di = try self.allocator.create(ObjectDebugInfo); + errdefer self.allocator.destroy(obj_di); + + try self.address_map.putNoClobber(ctx.name, obj_di); + + obj_di.* = .{ + .dwarf = try openElfDebugInfo(self.allocator, exe_mmap), + .mapped_memory = exe_mmap, + .base_address = ctx.base_address, + }; + + return obj_di; + } +}; + +const DIPContext = struct { + address: usize, + base_address: usize = undefined, + name: []const u8 = undefined, +}; + +fn dl_iterate_phdr_callback(info: *os.dl_phdr_info, size: usize, context: ?*DIPContext) callconv(.C) i32 { + const address = context.?.address; + + // The base address is too high + if (address < info.dlpi_addr) + return 0; + + const phdrs = info.dlpi_phdr[0..info.dlpi_phnum]; + for (phdrs) |*phdr| { + if (phdr.p_type != elf.PT_LOAD) continue; + + const seg_start = info.dlpi_addr + phdr.p_vaddr; + const seg_end = seg_start + phdr.p_memsz; + + if (address > seg_start and address <= seg_end) { + // Android libc uses NULL instead of an empty string to mark the + // main program + context.?.name = if (info.dlpi_name) |dlpi_name| + mem.toSliceConst(u8, dlpi_name) + else + ""; + context.?.base_address = info.dlpi_addr; + // Stop the iteration + return 1; + } + } + + // Continue the iteration + return 0; +} + +pub const ObjectDebugInfo = switch (builtin.os) { .macosx, .ios, .watchos, .tvos => struct { symbols: []const MachoSymbol, strings: []const u8, @@ -1213,6 +1329,11 @@ pub const DebugInfo = switch (builtin.os) { sect_contribs: []pdb.SectionContribEntry, modules: []Module, }, + .linux => struct { + base_address: usize, + dwarf: DW.DwarfInfo, + mapped_memory: []u8, + }, else => DW.DwarfInfo, }; From 99649794e965ce76c81cb91a7ff78f3a42d533bc Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 10 Feb 2020 13:18:28 +0100 Subject: [PATCH 03/99] win --- lib/std/debug.zig | 111 +++++++++++++++++++++++++++++++++++++++------- lib/std/fs.zig | 2 + 2 files changed, 96 insertions(+), 17 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index a36efe441b..8e43c7ccce 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -248,7 +248,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c } switch (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst)) { - 0 => { + 0, 1 => { const stderr = getStderrStream(); noasync stderr.print(format ++ "\n", args) catch os.abort(); if (trace) |t| { @@ -256,7 +256,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c } dumpCurrentStackTrace(first_trace_addr); }, - 1 => { + 2 => { // TODO detect if a different thread caused the panic, because in that case // we would want to return here instead of calling abort, so that the thread // which first called panic can finish printing a stack trace. @@ -404,13 +404,15 @@ pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: us /// TODO resources https://github.com/ziglang/zig/issues/4353 fn printSourceAtAddressWindows( - di: *DebugInfo, + di1: *DebugInfo, out_stream: var, relocated_address: usize, tty_config: TTY.Config, ) !void { + const di = try di1.lookupByAddress(relocated_address); + const allocator = getDebugInfoAllocator(); - const base_address = process.getBaseAddress(); + const base_address = di.base_address; const relative_address = relocated_address - base_address; var coff_section: *coff.Section = undefined; @@ -630,7 +632,7 @@ pub const TTY = struct { }; /// TODO resources https://github.com/ziglang/zig/issues/4353 -fn populateModule(di: *DebugInfo, mod: *Module) !void { +fn populateModule(di: *ObjectDebugInfo, mod: *Module) !void { if (mod.populated) return; const allocator = getDebugInfoAllocator(); @@ -824,27 +826,28 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) { return noasync root.os.debug.openSelfDebugInfo(allocator); } - if (builtin.os == .windows) { - return noasync openSelfDebugInfoWindows(allocator); - } if (comptime std.Target.current.isDarwin()) { return noasync openSelfDebugInfoMacOs(allocator); } - if (builtin.os == .linux) { + if (builtin.os == .linux or builtin.os == .windows) { _ = try allocator.create(u32); return DebugInfo.init(allocator); } return noasync openSelfDebugInfoPosix(allocator); } -fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { +fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !ObjectDebugInfo { const self_file = try fs.openSelfExe(); defer self_file.close(); + return openCoffDebugInfo(allocator, self_file); +} +fn openCoffDebugInfo(allocator: *mem.Allocator, self_file: fs.File) !ObjectDebugInfo { const coff_obj = try allocator.create(coff.Coff); coff_obj.* = coff.Coff.init(allocator, self_file); - var di = DebugInfo{ + var di = ObjectDebugInfo{ + .base_address = 0, .coff = coff_obj, .pdb = undefined, .sect_contribs = undefined, @@ -1202,24 +1205,97 @@ const MachoSymbol = struct { pub const DebugInfo = struct { allocator: *mem.Allocator, - address_map: std.StringHashMap(*ObjectDebugInfo), + address_map: std.AutoHashMap(usize, *ObjectDebugInfo), pub fn init(allocator: *mem.Allocator) DebugInfo { return DebugInfo{ .allocator = allocator, - .address_map = std.StringHashMap(*ObjectDebugInfo).init(allocator), + .address_map = std.AutoHashMap(usize, *ObjectDebugInfo).init(allocator), }; } pub fn deinit(self: *DebugInfo) void { + // XXX Free the values self.address_map.deinit(); } pub fn lookupByAddress(self: *DebugInfo, address: usize) !*ObjectDebugInfo { - const obj_di = try self.lookupModuleDl(address); + const obj_di = if (builtin.os == .windows) + try self.lookupModuleWin32(address) + else + try self.lookupModuleDl(address); return obj_di; } + fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ObjectDebugInfo { + const process_handle = windows.kernel32.GetCurrentProcess(); + + var modules: [32]windows.HMODULE = undefined; + var modules_needed: windows.DWORD = undefined; + // XXX Ask for the number of modules by passing size zero + if (windows.kernel32.K32EnumProcessModules( + process_handle, + @ptrCast([*]windows.HMODULE, &modules), + @sizeOf(@TypeOf(modules)), + &modules_needed, + ) == 0) + return error.DebugInfoNotFound; + + for (modules[0..modules_needed]) |module| { + var info: windows.MODULEINFO = undefined; + if (windows.kernel32.K32GetModuleInformation( + process_handle, + module, + &info, + @sizeOf(@TypeOf(info)), + ) == 0) + return error.DebugInfoNotFound; + + const seg_start = @ptrToInt(info.lpBaseOfDll); + const seg_end = seg_start + info.SizeOfImage; + + if (address >= seg_start and address < seg_end) { + var name_buffer: [windows.PATH_MAX_WIDE]u16 = [_]u16{0} ** windows.PATH_MAX_WIDE; + // XXX Use W variant + const len = windows.kernel32.K32GetModuleFileNameExW( + process_handle, + module, + @ptrCast(windows.LPWSTR, &name_buffer), + @sizeOf(@TypeOf(name_buffer)) / @sizeOf(u16), + ); + assert(len > 0); + const name_slice = mem.toSlice(u16, name_buffer[0..:0]); + warn("slice len {}\n", .{name_slice.len}); + + const x = try std.unicode.utf16leToUtf8Alloc(self.allocator, name_buffer[0..]); + warn("ask for {s} len {}\n", .{ x, name_slice.len }); + self.allocator.free(x); + + if (self.address_map.getValue(seg_start)) |obj_di| { + warn("cache hit!\n", .{}); + return obj_di; + } + + warn("loading?\n", .{}); + const file_obj = try fs.openFileAbsoluteW(name_slice, .{}); + errdefer file_obj.close(); + warn("loaded!\n", .{}); + + const obj_di = try self.allocator.create(ObjectDebugInfo); + errdefer self.allocator.destroy(obj_di); + + try self.address_map.putNoClobber(seg_start, obj_di); + + obj_di.* = try openCoffDebugInfo(self.allocator, file_obj); + obj_di.base_address = seg_start; + + return obj_di; + } + } + + return error.DebugInfoNotFound; + } + fn lookupModuleDl(self: *DebugInfo, address: usize) !*ObjectDebugInfo { var ctx = DIPContext{ .address = address }; @@ -1231,7 +1307,7 @@ pub const DebugInfo = struct { warn("found in \"{s}\"\n", .{ctx.name}); - if (self.address_map.getValue(ctx.name)) |obj_di| { + if (self.address_map.getValue(ctx.base_address)) |obj_di| { warn("cache hit!\n", .{}); return obj_di; } @@ -1257,7 +1333,7 @@ pub const DebugInfo = struct { const obj_di = try self.allocator.create(ObjectDebugInfo); errdefer self.allocator.destroy(obj_di); - try self.address_map.putNoClobber(ctx.name, obj_di); + try self.address_map.putNoClobber(ctx.base_address, obj_di); obj_di.* = .{ .dwarf = try openElfDebugInfo(self.allocator, exe_mmap), @@ -1289,7 +1365,7 @@ fn dl_iterate_phdr_callback(info: *os.dl_phdr_info, size: usize, context: ?*DIPC const seg_start = info.dlpi_addr + phdr.p_vaddr; const seg_end = seg_start + phdr.p_memsz; - if (address > seg_start and address <= seg_end) { + if (address >= seg_start and address < seg_end) { // Android libc uses NULL instead of an empty string to mark the // main program context.?.name = if (info.dlpi_name) |dlpi_name| @@ -1324,6 +1400,7 @@ pub const ObjectDebugInfo = switch (builtin.os) { } }, .uefi, .windows => struct { + base_address: usize, pdb: pdb.Pdb, coff: *coff.Coff, sect_contribs: []pdb.SectionContribEntry, diff --git a/lib/std/fs.zig b/lib/std/fs.zig index c3d418eb8c..72a60aa97e 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -859,6 +859,8 @@ pub const Dir = struct { null, 0, ); + std.debug.warn("here? rc {x}\n", .{rc}); + switch (rc) { .SUCCESS => return result, .OBJECT_NAME_INVALID => unreachable, From 463f70443164ae602eb658c2bed557c289da020f Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 10 Feb 2020 17:19:19 +0100 Subject: [PATCH 04/99] wide --- lib/std/debug.zig | 24 +++++++++--------------- lib/std/fs.zig | 2 -- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 8e43c7ccce..69ee3f1550 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1255,31 +1255,25 @@ pub const DebugInfo = struct { const seg_end = seg_start + info.SizeOfImage; if (address >= seg_start and address < seg_end) { - var name_buffer: [windows.PATH_MAX_WIDE]u16 = [_]u16{0} ** windows.PATH_MAX_WIDE; - // XXX Use W variant - const len = windows.kernel32.K32GetModuleFileNameExW( + var name_buffer: [windows.MAX_PATH]u8 = undefined; + // XXX: Use W variant (#534) + const len = windows.kernel32.K32GetModuleFileNameExA( process_handle, module, - @ptrCast(windows.LPWSTR, &name_buffer), - @sizeOf(@TypeOf(name_buffer)) / @sizeOf(u16), + @ptrCast(windows.LPSTR, &name_buffer), + @sizeOf(@TypeOf(name_buffer)) / @sizeOf(u8), ); assert(len > 0); - const name_slice = mem.toSlice(u16, name_buffer[0..:0]); - warn("slice len {}\n", .{name_slice.len}); - - const x = try std.unicode.utf16leToUtf8Alloc(self.allocator, name_buffer[0..]); - warn("ask for {s} len {}\n", .{ x, name_slice.len }); - self.allocator.free(x); if (self.address_map.getValue(seg_start)) |obj_di| { - warn("cache hit!\n", .{}); return obj_di; } - warn("loading?\n", .{}); - const file_obj = try fs.openFileAbsoluteW(name_slice, .{}); + // XXX: The compiler segfaults if the slicing is done as a + // parameter + const tmp = name_buffer[0..:0]; + const file_obj = try fs.cwd().openFileC(tmp, .{}); errdefer file_obj.close(); - warn("loaded!\n", .{}); const obj_di = try self.allocator.create(ObjectDebugInfo); errdefer self.allocator.destroy(obj_di); diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 72a60aa97e..c3d418eb8c 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -859,8 +859,6 @@ pub const Dir = struct { null, 0, ); - std.debug.warn("here? rc {x}\n", .{rc}); - switch (rc) { .SUCCESS => return result, .OBJECT_NAME_INVALID => unreachable, From b3f728585a06d652393ca6d3feaaac5a42bfa968 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 10 Feb 2020 20:47:07 +0100 Subject: [PATCH 05/99] windows widestring --- lib/std/debug.zig | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 69ee3f1550..20c75d4a8c 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1255,13 +1255,14 @@ pub const DebugInfo = struct { const seg_end = seg_start + info.SizeOfImage; if (address >= seg_start and address < seg_end) { - var name_buffer: [windows.MAX_PATH]u8 = undefined; - // XXX: Use W variant (#534) - const len = windows.kernel32.K32GetModuleFileNameExA( + var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined; + // openFileAbsoluteW requires the prefix to be present + mem.copy(u16, name_buffer[0..4], &[_]u16{ '\\', '?', '?', '\\' }); + const len = windows.kernel32.K32GetModuleFileNameExW( process_handle, module, - @ptrCast(windows.LPSTR, &name_buffer), - @sizeOf(@TypeOf(name_buffer)) / @sizeOf(u8), + @ptrCast(windows.LPWSTR, &name_buffer[4]), + windows.PATH_MAX_WIDE, ); assert(len > 0); @@ -1270,9 +1271,9 @@ pub const DebugInfo = struct { } // XXX: The compiler segfaults if the slicing is done as a - // parameter + // parameter (#4423) const tmp = name_buffer[0..:0]; - const file_obj = try fs.cwd().openFileC(tmp, .{}); + const file_obj = try fs.openFileAbsoluteW(tmp, .{}); errdefer file_obj.close(); const obj_di = try self.allocator.create(ObjectDebugInfo); @@ -1293,8 +1294,6 @@ pub const DebugInfo = struct { fn lookupModuleDl(self: *DebugInfo, address: usize) !*ObjectDebugInfo { var ctx = DIPContext{ .address = address }; - warn("lookup {x}\n", .{address}); - // XXX Locking? if (os.dl_iterate_phdr(DIPContext, dl_iterate_phdr_callback, &ctx) == 0) return error.DebugInfoNotFound; From 2a350cf1745f08c98c5897ce2d27627d28ea00da Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 11 Feb 2020 09:40:21 +0100 Subject: [PATCH 06/99] osx --- lib/std/debug.zig | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 20c75d4a8c..31a96d2089 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -696,8 +696,10 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach return null; } -fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { - const base_addr = process.getBaseAddress(); +fn printSourceAtAddressMacOs(di1: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { + const di = try di1.lookupByAddress(address); + + const base_addr = di.base_address; const adjusted_addr = 0x100000000 + (address - base_addr); const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse { @@ -826,10 +828,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) { return noasync root.os.debug.openSelfDebugInfo(allocator); } - if (comptime std.Target.current.isDarwin()) { - return noasync openSelfDebugInfoMacOs(allocator); - } - if (builtin.os == .linux or builtin.os == .windows) { + if (builtin.os == .linux or builtin.os == .windows or builtin.os == .macosx) { _ = try allocator.create(u32); return DebugInfo.init(allocator); } @@ -1220,13 +1219,34 @@ pub const DebugInfo = struct { } pub fn lookupByAddress(self: *DebugInfo, address: usize) !*ObjectDebugInfo { - const obj_di = if (builtin.os == .windows) + const obj_di = if (comptime std.Target.current.isDarwin()) + try self.lookupModuleDyld(address) + else if (builtin.os == .windows) try self.lookupModuleWin32(address) else try self.lookupModuleDl(address); return obj_di; } + fn lookupModuleDyld(self: *DebugInfo, address: usize) !*ObjectDebugInfo { + const image_count = std.c._dyld_image_count(); + + var i: u32 = 0; + while (i < image_count) : (i += 1) { + const header = std.c._dyld_get_image_header(i) orelse continue; + // The array of load commands is right after the header + var cmd_ptr = @intToPtr([*]u8, @ptrToInt(header) + @sizeOf(macho.mach_header_64)); + const cmd_ptr_end = cmd_ptr + header.sizeofcmds; + + var cmd = + for (cmds) |*lc| { + if (lc.cmd != macho.LC_SEGMENT_64) continue; + + const segment_cmd = @ptrCast(*macho.segment_command_64, lc); + } + } + } + fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ObjectDebugInfo { const process_handle = windows.kernel32.GetCurrentProcess(); @@ -1298,10 +1318,7 @@ pub const DebugInfo = struct { if (os.dl_iterate_phdr(DIPContext, dl_iterate_phdr_callback, &ctx) == 0) return error.DebugInfoNotFound; - warn("found in \"{s}\"\n", .{ctx.name}); - if (self.address_map.getValue(ctx.base_address)) |obj_di| { - warn("cache hit!\n", .{}); return obj_di; } @@ -1377,6 +1394,7 @@ fn dl_iterate_phdr_callback(info: *os.dl_phdr_info, size: usize, context: ?*DIPC pub const ObjectDebugInfo = switch (builtin.os) { .macosx, .ios, .watchos, .tvos => struct { + base_address: usize, symbols: []const MachoSymbol, strings: []const u8, ofiles: OFileTable, From b9c02e4076204c49500fd1d0dcb91420aed17f03 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 18 Feb 2020 12:37:25 +0100 Subject: [PATCH 07/99] elvis entered the building --- lib/std/debug.zig | 199 ++++++++++++++++++++++++++++++---------------- 1 file changed, 130 insertions(+), 69 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 31a96d2089..f7e1be3733 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -700,7 +700,8 @@ fn printSourceAtAddressMacOs(di1: *DebugInfo, out_stream: var, address: usize, t const di = try di1.lookupByAddress(address); const base_addr = di.base_address; - const adjusted_addr = 0x100000000 + (address - base_addr); + const adjusted_addr = address - base_addr; + assert(adjusted_addr >= 0x100000000); const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse { return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); @@ -734,7 +735,6 @@ pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, addres const module = try debug_info.lookupByAddress(address); const reloc_address = address - module.base_address; - warn("reloc {x} => {x}\n", .{ address, reloc_address }); if (module.dwarf.findCompileUnit(reloc_address) catch null) |compile_unit| { const compile_unit_name = try compile_unit.die.getAttrString(&module.dwarf, DW.AT_name); @@ -1078,29 +1078,33 @@ fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DW.DwarfInfo { } /// TODO resources https://github.com/ziglang/zig/issues/4353 -fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { - const hdr = &std.c._mh_execute_header; +fn openMachODebugInfo(allocator: *mem.Allocator, memmo: []const u8) !ObjectDebugInfo { + // const hdr = &std.c._mh_execute_header; + const hdr = @ptrCast( + *const macho.mach_header_64, + @alignCast(@alignOf(macho.mach_header_64), memmo.ptr), + ); assert(hdr.magic == std.macho.MH_MAGIC_64); - const hdr_base = @ptrCast([*]u8, hdr); + const hdr_base = @ptrCast([*]const u8, hdr); var ptr = hdr_base + @sizeOf(macho.mach_header_64); var ncmd: u32 = hdr.ncmds; const symtab = while (ncmd != 0) : (ncmd -= 1) { - const lc = @ptrCast(*std.macho.load_command, ptr); + const lc = @ptrCast(*const std.macho.load_command, ptr); switch (lc.cmd) { - std.macho.LC_SYMTAB => break @ptrCast(*std.macho.symtab_command, ptr), + std.macho.LC_SYMTAB => break @ptrCast(*const std.macho.symtab_command, ptr), else => {}, } ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize); } else { return error.MissingDebugInfo; }; - const syms = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms]; - const strings = @ptrCast([*]u8, hdr_base + symtab.stroff)[0..symtab.strsize]; + const syms = @ptrCast([*]const macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms]; + const strings = @ptrCast([*]const u8, hdr_base + symtab.stroff)[0..symtab.strsize]; const symbols_buf = try allocator.alloc(MachoSymbol, syms.len); - var ofile: ?*macho.nlist_64 = null; + var ofile: ?*const macho.nlist_64 = null; var reloc: u64 = 0; var symbol_index: usize = 0; var last_len: u64 = 0; @@ -1148,8 +1152,10 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { // This sort is so that we can binary search later. std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan); - return DebugInfo{ - .ofiles = DebugInfo.OFileTable.init(allocator), + return ObjectDebugInfo{ + .base_address = undefined, + .mapped_memory = undefined, + .ofiles = ObjectDebugInfo.OFileTable.init(allocator), .symbols = symbols, .strings = strings, }; @@ -1188,8 +1194,8 @@ fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { } const MachoSymbol = struct { - nlist: *macho.nlist_64, - ofile: ?*macho.nlist_64, + nlist: *const macho.nlist_64, + ofile: ?*const macho.nlist_64, reloc: u64, /// Returns the address from the macho file @@ -1233,18 +1239,68 @@ pub const DebugInfo = struct { var i: u32 = 0; while (i < image_count) : (i += 1) { + const base_address = std.c._dyld_get_image_vmaddr_slide(i); + + if (address < base_address) continue; + const header = std.c._dyld_get_image_header(i) orelse continue; // The array of load commands is right after the header var cmd_ptr = @intToPtr([*]u8, @ptrToInt(header) + @sizeOf(macho.mach_header_64)); - const cmd_ptr_end = cmd_ptr + header.sizeofcmds; - var cmd = - for (cmds) |*lc| { + var cmds = header.ncmds; + while (cmds != 0) : (cmds -= 1) { + const lc = @ptrCast( + *macho.load_command, + @alignCast(@alignOf(macho.load_command), cmd_ptr), + ); + cmd_ptr += lc.cmdsize; if (lc.cmd != macho.LC_SEGMENT_64) continue; - const segment_cmd = @ptrCast(*macho.segment_command_64, lc); + const segment_cmd = @ptrCast( + *const std.macho.segment_command_64, + @alignCast(@alignOf(std.macho.segment_command_64), lc), + ); + + const rebased_address = address - base_address; + const seg_start = segment_cmd.vmaddr; + const seg_end = seg_start + segment_cmd.vmsize; + + if (rebased_address >= seg_start and rebased_address < seg_end) { + if (self.address_map.getValue(base_address)) |obj_di| { + return obj_di; + } + + const image_name = std.c._dyld_get_image_name(i); + const exe_file = try fs.openFileAbsoluteC(image_name, .{}); + errdefer exe_file.close(); + + const exe_len = math.cast(usize, try exe_file.getEndPos()) catch + return error.DebugInfoTooLarge; + const exe_mmap = try os.mmap( + null, + exe_len, + os.PROT_READ, + os.MAP_SHARED, + exe_file.handle, + 0, + ); + errdefer os.munmap(exe_mmap); + + const obj_di = try self.allocator.create(ObjectDebugInfo); + errdefer self.allocator.destroy(obj_di); + + try self.address_map.putNoClobber(base_address, obj_di); + + obj_di.* = try openMachODebugInfo(self.allocator, exe_mmap); + obj_di.base_address = base_address; + obj_di.mapped_memory = exe_mmap; + + return obj_di; + } } } + + return error.DebugInfoNotFound; } fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ObjectDebugInfo { @@ -1252,7 +1308,8 @@ pub const DebugInfo = struct { var modules: [32]windows.HMODULE = undefined; var modules_needed: windows.DWORD = undefined; - // XXX Ask for the number of modules by passing size zero + // TODO: Ask for the number of modules by passing size zero, 32 ought to + // be enough for everyone in the meanwhile if (windows.kernel32.K32EnumProcessModules( process_handle, @ptrCast([*]windows.HMODULE, &modules), @@ -1275,6 +1332,10 @@ pub const DebugInfo = struct { const seg_end = seg_start + info.SizeOfImage; if (address >= seg_start and address < seg_end) { + if (self.address_map.getValue(seg_start)) |obj_di| { + return obj_di; + } + var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined; // openFileAbsoluteW requires the prefix to be present mem.copy(u16, name_buffer[0..4], &[_]u16{ '\\', '?', '?', '\\' }); @@ -1286,12 +1347,8 @@ pub const DebugInfo = struct { ); assert(len > 0); - if (self.address_map.getValue(seg_start)) |obj_di| { - return obj_di; - } - - // XXX: The compiler segfaults if the slicing is done as a - // parameter (#4423) + // The compiler segfaults if the slicing is done as a parameter + // (#4423) const tmp = name_buffer[0..:0]; const file_obj = try fs.openFileAbsoluteW(tmp, .{}); errdefer file_obj.close(); @@ -1312,11 +1369,49 @@ pub const DebugInfo = struct { } fn lookupModuleDl(self: *DebugInfo, address: usize) !*ObjectDebugInfo { - var ctx = DIPContext{ .address = address }; + var ctx: struct { + // Input + address: usize, + // Output + base_address: usize = undefined, + name: []const u8 = undefined, + } = .{ .address = address }; + const CtxTy = @TypeOf(ctx); - // XXX Locking? - if (os.dl_iterate_phdr(DIPContext, dl_iterate_phdr_callback, &ctx) == 0) + if (os.dl_iterate_phdr(&ctx, anyerror, struct { + fn callback(info: *os.dl_phdr_info, size: usize, context: *CtxTy) !void { + // The base address is too high + if (context.address < info.dlpi_addr) + return; + + const phdrs = info.dlpi_phdr[0..info.dlpi_phnum]; + for (phdrs) |*phdr| { + if (phdr.p_type != elf.PT_LOAD) continue; + + const seg_start = info.dlpi_addr + phdr.p_vaddr; + const seg_end = seg_start + phdr.p_memsz; + + if (context.address >= seg_start and context.address < seg_end) { + // Android libc uses NULL instead of an empty string to mark the + // main program + context.name = if (info.dlpi_name) |dlpi_name| + mem.toSliceConst(u8, dlpi_name) + else + ""; + context.base_address = info.dlpi_addr; + // Stop the iteration + return error.Found; + } + } + } + }.callback)) { return error.DebugInfoNotFound; + } else |err| { + switch (err) { + error.Found => {}, + else => return error.DebugInfoNotFound, + } + } if (self.address_map.getValue(ctx.base_address)) |obj_di| { return obj_di; @@ -1355,58 +1450,24 @@ pub const DebugInfo = struct { } }; -const DIPContext = struct { - address: usize, - base_address: usize = undefined, - name: []const u8 = undefined, -}; - -fn dl_iterate_phdr_callback(info: *os.dl_phdr_info, size: usize, context: ?*DIPContext) callconv(.C) i32 { - const address = context.?.address; - - // The base address is too high - if (address < info.dlpi_addr) - return 0; - - const phdrs = info.dlpi_phdr[0..info.dlpi_phnum]; - for (phdrs) |*phdr| { - if (phdr.p_type != elf.PT_LOAD) continue; - - const seg_start = info.dlpi_addr + phdr.p_vaddr; - const seg_end = seg_start + phdr.p_memsz; - - if (address >= seg_start and address < seg_end) { - // Android libc uses NULL instead of an empty string to mark the - // main program - context.?.name = if (info.dlpi_name) |dlpi_name| - mem.toSliceConst(u8, dlpi_name) - else - ""; - context.?.base_address = info.dlpi_addr; - // Stop the iteration - return 1; - } - } - - // Continue the iteration - return 0; -} +const DIPContext = struct {}; pub const ObjectDebugInfo = switch (builtin.os) { .macosx, .ios, .watchos, .tvos => struct { base_address: usize, + mapped_memory: []u8, symbols: []const MachoSymbol, strings: []const u8, ofiles: OFileTable, const OFileTable = std.HashMap( - *macho.nlist_64, + *const macho.nlist_64, DW.DwarfInfo, - std.hash_map.getHashPtrAddrFn(*macho.nlist_64), - std.hash_map.getTrivialEqlFn(*macho.nlist_64), + std.hash_map.getHashPtrAddrFn(*const macho.nlist_64), + std.hash_map.getTrivialEqlFn(*const macho.nlist_64), ); - pub fn allocator(self: DebugInfo) *mem.Allocator { + pub fn allocator(self: @This()) *mem.Allocator { return self.ofiles.allocator; } }, @@ -1426,7 +1487,7 @@ pub const ObjectDebugInfo = switch (builtin.os) { }; /// TODO resources https://github.com/ziglang/zig/issues/4353 -fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, address: usize) !LineInfo { +fn getLineNumberInfoMacOs(di: *ObjectDebugInfo, symbol: MachoSymbol, address: usize) !LineInfo { const ofile = symbol.ofile orelse return error.MissingDebugInfo; const gop = try di.ofiles.getOrPut(ofile); const dwarf_info = if (gop.found_existing) &gop.kv.value else blk: { From fabab58528f4b55378418d838c8d56b7c7da5015 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 18 Feb 2020 13:06:17 +0100 Subject: [PATCH 08/99] less hideous --- lib/std/debug.zig | 78 +++++++++++++++++++---------------------------- lib/std/dwarf.zig | 10 +++--- 2 files changed, 36 insertions(+), 52 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index f7e1be3733..c50c7f1da1 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -822,23 +822,20 @@ pub const OpenSelfDebugInfoError = error{ /// TODO resources https://github.com/ziglang/zig/issues/4353 /// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented, /// make this `noasync fn` and remove the individual noasync calls. -pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { +pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { if (builtin.strip_debug_info) return error.MissingDebugInfo; if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) { return noasync root.os.debug.openSelfDebugInfo(allocator); } - if (builtin.os == .linux or builtin.os == .windows or builtin.os == .macosx) { - _ = try allocator.create(u32); - return DebugInfo.init(allocator); + switch (builtin.os) { + .linux, + .freebsd, + .macosx, + .windows, + => return DebugInfo.init(allocator), + else => @compileError("openSelfDebugInfo unsupported for this platform"), } - return noasync openSelfDebugInfoPosix(allocator); -} - -fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !ObjectDebugInfo { - const self_file = try fs.openSelfExe(); - defer self_file.close(); - return openCoffDebugInfo(allocator, self_file); } fn openCoffDebugInfo(allocator: *mem.Allocator, self_file: fs.File) !ObjectDebugInfo { @@ -846,7 +843,7 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, self_file: fs.File) !ObjectDebug coff_obj.* = coff.Coff.init(allocator, self_file); var di = ObjectDebugInfo{ - .base_address = 0, + .base_address = undefined, .coff = coff_obj, .pdb = undefined, .sect_contribs = undefined, @@ -1018,10 +1015,16 @@ fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DW.DwarfInfo. }; } +fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 { + const start = try math.cast(usize, offset); + const end = start + try math.cast(usize, size); + return ptr[start..end]; +} + /// TODO resources https://github.com/ziglang/zig/issues/4353 pub fn openElfDebugInfo( allocator: *mem.Allocator, - data: []u8, + data: []const u8, ) !DW.DwarfInfo { var seekable_stream = io.SliceSeekableInStream.init(data); var efile = try elf.Elf.openStream( @@ -1043,12 +1046,12 @@ pub fn openElfDebugInfo( var di = DW.DwarfInfo{ .endian = efile.endian, - .debug_info = (data[@intCast(usize, debug_info.sh_offset)..@intCast(usize, debug_info.sh_offset + debug_info.sh_size)]), - .debug_abbrev = (data[@intCast(usize, debug_abbrev.sh_offset)..@intCast(usize, debug_abbrev.sh_offset + debug_abbrev.sh_size)]), - .debug_str = (data[@intCast(usize, debug_str.sh_offset)..@intCast(usize, debug_str.sh_offset + debug_str.sh_size)]), - .debug_line = (data[@intCast(usize, debug_line.sh_offset)..@intCast(usize, debug_line.sh_offset + debug_line.sh_size)]), + .debug_info = try chopSlice(data, debug_info.sh_offset, debug_info.sh_size), + .debug_abbrev = try chopSlice(data, debug_abbrev.sh_offset, debug_abbrev.sh_size), + .debug_str = try chopSlice(data, debug_str.sh_offset, debug_str.sh_size), + .debug_line = try chopSlice(data, debug_line.sh_offset, debug_line.sh_size), .debug_ranges = if (opt_debug_ranges) |debug_ranges| - data[@intCast(usize, debug_ranges.sh_offset)..@intCast(usize, debug_ranges.sh_offset + debug_ranges.sh_size)] + try chopSlice(data, debug_ranges.sh_offset, debug_ranges.sh_size) else null, }; @@ -1057,26 +1060,6 @@ pub fn openElfDebugInfo( return di; } -/// TODO resources https://github.com/ziglang/zig/issues/4353 -fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DW.DwarfInfo { - var exe_file = try fs.openSelfExe(); - errdefer exe_file.close(); - - const exe_len = math.cast(usize, try exe_file.getEndPos()) catch - return error.DebugInfoTooLarge; - const exe_mmap = try os.mmap( - null, - exe_len, - os.PROT_READ, - os.MAP_SHARED, - exe_file.handle, - 0, - ); - errdefer os.munmap(exe_mmap); - - return openElfDebugInfo(allocator, exe_mmap); -} - /// TODO resources https://github.com/ziglang/zig/issues/4353 fn openMachODebugInfo(allocator: *mem.Allocator, memmo: []const u8) !ObjectDebugInfo { // const hdr = &std.c._mh_execute_header; @@ -1220,7 +1203,7 @@ pub const DebugInfo = struct { } pub fn deinit(self: *DebugInfo) void { - // XXX Free the values + // TODO: resources https://github.com/ziglang/zig/issues/4353 self.address_map.deinit(); } @@ -1450,8 +1433,6 @@ pub const DebugInfo = struct { } }; -const DIPContext = struct {}; - pub const ObjectDebugInfo = switch (builtin.os) { .macosx, .ios, .watchos, .tvos => struct { base_address: usize, @@ -1540,7 +1521,10 @@ fn getLineNumberInfoMacOs(di: *ObjectDebugInfo, symbol: MachoSymbol, address: us var opt_debug_str: ?*const macho.section_64 = null; var opt_debug_ranges: ?*const macho.section_64 = null; - const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects]; + const sections = @ptrCast( + [*]const macho.section_64, + @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)), + )[0..segcmd.nsects]; for (sections) |*sect| { // The section name may not exceed 16 chars and a trailing null may // not be present @@ -1573,12 +1557,12 @@ fn getLineNumberInfoMacOs(di: *ObjectDebugInfo, symbol: MachoSymbol, address: us gop.kv.value = DW.DwarfInfo{ .endian = .Little, - .debug_info = exe_mmap[@intCast(usize, debug_info.offset)..@intCast(usize, debug_info.offset + debug_info.size)], - .debug_abbrev = exe_mmap[@intCast(usize, debug_abbrev.offset)..@intCast(usize, debug_abbrev.offset + debug_abbrev.size)], - .debug_str = exe_mmap[@intCast(usize, debug_str.offset)..@intCast(usize, debug_str.offset + debug_str.size)], - .debug_line = exe_mmap[@intCast(usize, debug_line.offset)..@intCast(usize, debug_line.offset + debug_line.size)], + .debug_info = try chopSlice(exe_mmap, debug_info.offset, debug_info.size), + .debug_abbrev = try chopSlice(exe_mmap, debug_abbrev.offset, debug_abbrev.size), + .debug_str = try chopSlice(exe_mmap, debug_str.offset, debug_str.size), + .debug_line = try chopSlice(exe_mmap, debug_line.offset, debug_line.size), .debug_ranges = if (opt_debug_ranges) |debug_ranges| - exe_mmap[@intCast(usize, debug_ranges.offset)..@intCast(usize, debug_ranges.offset + debug_ranges.size)] + try chopSlice(exe_mmap, debug_ranges.offset, debug_ranges.size) else null, }; diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index cb5bb3ec8a..47727eeeeb 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -387,11 +387,11 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con pub const DwarfInfo = struct { endian: builtin.Endian, // No memory is owned by the DwarfInfo - debug_info: []u8, - debug_abbrev: []u8, - debug_str: []u8, - debug_line: []u8, - debug_ranges: ?[]u8, + debug_info: []const u8, + debug_abbrev: []const u8, + debug_str: []const u8, + debug_line: []const u8, + debug_ranges: ?[]const u8, // Filled later by the initializer abbrev_table_list: ArrayList(AbbrevTableHeader) = undefined, compile_unit_list: ArrayList(CompileUnit) = undefined, From d5b583008ad477c292db4f783072605f5d28f843 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 18 Feb 2020 16:19:23 +0100 Subject: [PATCH 09/99] sudoku --- lib/std/debug.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index c50c7f1da1..9ce667ee44 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1459,7 +1459,7 @@ pub const ObjectDebugInfo = switch (builtin.os) { sect_contribs: []pdb.SectionContribEntry, modules: []Module, }, - .linux => struct { + .linux, .freebsd => struct { base_address: usize, dwarf: DW.DwarfInfo, mapped_memory: []u8, From ce2efbdca6f1bf8a1ac01c379cb90990a2ea4e78 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 18 Feb 2020 19:03:31 +0100 Subject: [PATCH 10/99] Correctly count all the loaded modules on Windows --- lib/std/debug.zig | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 9ce667ee44..c3c8a2cc95 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -248,7 +248,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c } switch (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst)) { - 0, 1 => { + 0 => { const stderr = getStderrStream(); noasync stderr.print(format ++ "\n", args) catch os.abort(); if (trace) |t| { @@ -256,7 +256,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c } dumpCurrentStackTrace(first_trace_addr); }, - 2 => { + 1 => { // TODO detect if a different thread caused the panic, because in that case // we would want to return here instead of calling abort, so that the thread // which first called panic can finish printing a stack trace. @@ -1289,19 +1289,37 @@ pub const DebugInfo = struct { fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ObjectDebugInfo { const process_handle = windows.kernel32.GetCurrentProcess(); - var modules: [32]windows.HMODULE = undefined; - var modules_needed: windows.DWORD = undefined; - // TODO: Ask for the number of modules by passing size zero, 32 ought to - // be enough for everyone in the meanwhile + // Find how many modules are actually loaded + var dummy: windows.HMODULE = undefined; + var bytes_needed: windows.DWORD = undefined; if (windows.kernel32.K32EnumProcessModules( process_handle, - @ptrCast([*]windows.HMODULE, &modules), - @sizeOf(@TypeOf(modules)), - &modules_needed, + @ptrCast([*]windows.HMODULE, &dummy), + 0, + &bytes_needed, ) == 0) return error.DebugInfoNotFound; - for (modules[0..modules_needed]) |module| { + const needed_modules = bytes_needed / @sizeOf(windows.HMODULE); + + // Fetch the complete module list + var modules = try self.allocator.alloc(windows.HMODULE, needed_modules); + defer self.allocator.free(modules); + if (windows.kernel32.K32EnumProcessModules( + process_handle, + modules.ptr, + try math.cast(windows.DWORD, modules.len * @sizeOf(windows.HMODULE)), + &bytes_needed, + ) == 0) + return error.DebugInfoNotFound; + + // There's an unavoidable TOCTOU problem here, the module list may have + // changed between the two EnumProcessModules call. + // Pick the smallest amount of elements to avoid processing garbage. + const needed_modules_after = bytes_needed / @sizeOf(windows.HMODULE); + const loaded_modules = math.min(needed_modules, needed_modules_after); + + for (modules[0..loaded_modules]) |module| { var info: windows.MODULEINFO = undefined; if (windows.kernel32.K32GetModuleInformation( process_handle, From 342a2749487d9952cf2afd6f8fb3e9534d4a210a Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 20 Feb 2020 20:18:51 +0100 Subject: [PATCH 11/99] tidy interface, const correctness --- lib/std/debug.zig | 139 ++++++++++++++++++++-------------------------- lib/std/dwarf.zig | 10 ++-- 2 files changed, 65 insertions(+), 84 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index c3c8a2cc95..1e5a80d1fb 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -838,9 +838,13 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { } } -fn openCoffDebugInfo(allocator: *mem.Allocator, self_file: fs.File) !ObjectDebugInfo { +/// TODO resources https://github.com/ziglang/zig/issues/4353 +fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ObjectDebugInfo { + const coff_file = try std.fs.openFileAbsoluteW(coff_file_path.ptr, .{}); + errdefer coff_file.close(); + const coff_obj = try allocator.create(coff.Coff); - coff_obj.* = coff.Coff.init(allocator, self_file); + coff_obj.* = coff.Coff.init(allocator, coff_file); var di = ObjectDebugInfo{ .base_address = undefined, @@ -1007,14 +1011,6 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { return list.toOwnedSlice(); } -fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DW.DwarfInfo.Section { - const elf_header = (try elf_file.findSection(name)) orelse return null; - return DW.DwarfInfo.Section{ - .offset = elf_header.sh_offset, - .size = elf_header.sh_size, - }; -} - fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 { const start = try math.cast(usize, offset); const end = start + try math.cast(usize, size); @@ -1022,11 +1018,10 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 { } /// TODO resources https://github.com/ziglang/zig/issues/4353 -pub fn openElfDebugInfo( - allocator: *mem.Allocator, - data: []const u8, -) !DW.DwarfInfo { - var seekable_stream = io.SliceSeekableInStream.init(data); +pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ObjectDebugInfo { + const mapped_mem = mapWholeFile(elf_file_path) catch |_| return error.InvalidDebugInfo; + + var seekable_stream = io.SliceSeekableInStream.init(mapped_mem); var efile = try elf.Elf.openStream( allocator, @ptrCast(*DW.DwarfSeekableStream, &seekable_stream.seekable_stream), @@ -1046,26 +1041,32 @@ pub fn openElfDebugInfo( var di = DW.DwarfInfo{ .endian = efile.endian, - .debug_info = try chopSlice(data, debug_info.sh_offset, debug_info.sh_size), - .debug_abbrev = try chopSlice(data, debug_abbrev.sh_offset, debug_abbrev.sh_size), - .debug_str = try chopSlice(data, debug_str.sh_offset, debug_str.sh_size), - .debug_line = try chopSlice(data, debug_line.sh_offset, debug_line.sh_size), + .debug_info = try chopSlice(mapped_mem, debug_info.sh_offset, debug_info.sh_size), + .debug_abbrev = try chopSlice(mapped_mem, debug_abbrev.sh_offset, debug_abbrev.sh_size), + .debug_str = try chopSlice(mapped_mem, debug_str.sh_offset, debug_str.sh_size), + .debug_line = try chopSlice(mapped_mem, debug_line.sh_offset, debug_line.sh_size), .debug_ranges = if (opt_debug_ranges) |debug_ranges| - try chopSlice(data, debug_ranges.sh_offset, debug_ranges.sh_size) + try chopSlice(mapped_mem, debug_ranges.sh_offset, debug_ranges.sh_size) else null, }; try DW.openDwarfDebugInfo(&di, allocator); - return di; + + return ObjectDebugInfo{ + .base_address = undefined, + .dwarf = di, + .mapped_memory = mapped_mem, + }; } /// TODO resources https://github.com/ziglang/zig/issues/4353 -fn openMachODebugInfo(allocator: *mem.Allocator, memmo: []const u8) !ObjectDebugInfo { - // const hdr = &std.c._mh_execute_header; +fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !ObjectDebugInfo { + const mapped_mem = mapWholeFile(macho_file_path) catch |_| return error.InvalidDebugInfo; + const hdr = @ptrCast( *const macho.mach_header_64, - @alignCast(@alignOf(macho.mach_header_64), memmo.ptr), + @alignCast(@alignOf(macho.mach_header_64), mapped_mem.ptr), ); assert(hdr.magic == std.macho.MH_MAGIC_64); @@ -1137,7 +1138,7 @@ fn openMachODebugInfo(allocator: *mem.Allocator, memmo: []const u8) !ObjectDebug return ObjectDebugInfo{ .base_address = undefined, - .mapped_memory = undefined, + .mapped_memory = mapped_mem, .ofiles = ObjectDebugInfo.OFileTable.init(allocator), .symbols = symbols, .strings = strings, @@ -1191,6 +1192,24 @@ const MachoSymbol = struct { } }; +fn mapWholeFile(path: []const u8) ![]const u8 { + const file = try fs.openFileAbsolute(path, .{}); + defer file.close(); + + const file_len = try math.cast(usize, try file.getEndPos()); + const mapped_mem = try os.mmap( + null, + file_len, + os.PROT_READ, + os.MAP_SHARED, + file.handle, + 0, + ); + errdefer os.munmap(mapped_mem); + + return mapped_mem; +} + pub const DebugInfo = struct { allocator: *mem.Allocator, address_map: std.AutoHashMap(usize, *ObjectDebugInfo), @@ -1253,30 +1272,14 @@ pub const DebugInfo = struct { return obj_di; } - const image_name = std.c._dyld_get_image_name(i); - const exe_file = try fs.openFileAbsoluteC(image_name, .{}); - errdefer exe_file.close(); - - const exe_len = math.cast(usize, try exe_file.getEndPos()) catch - return error.DebugInfoTooLarge; - const exe_mmap = try os.mmap( - null, - exe_len, - os.PROT_READ, - os.MAP_SHARED, - exe_file.handle, - 0, - ); - errdefer os.munmap(exe_mmap); - const obj_di = try self.allocator.create(ObjectDebugInfo); errdefer self.allocator.destroy(obj_di); try self.address_map.putNoClobber(base_address, obj_di); - obj_di.* = try openMachODebugInfo(self.allocator, exe_mmap); + const macho_path = mem.toSliceConst(u8, std.c._dyld_get_image_name(i)); + obj_di.* = try openMachODebugInfo(self.allocator, macho_path); obj_di.base_address = base_address; - obj_di.mapped_memory = exe_mmap; return obj_di; } @@ -1348,18 +1351,12 @@ pub const DebugInfo = struct { ); assert(len > 0); - // The compiler segfaults if the slicing is done as a parameter - // (#4423) - const tmp = name_buffer[0..:0]; - const file_obj = try fs.openFileAbsoluteW(tmp, .{}); - errdefer file_obj.close(); - const obj_di = try self.allocator.create(ObjectDebugInfo); errdefer self.allocator.destroy(obj_di); try self.address_map.putNoClobber(seg_start, obj_di); - obj_di.* = try openCoffDebugInfo(self.allocator, file_obj); + obj_di.* = try openCoffDebugInfo(self.allocator, name_buffer[0..:0]); obj_di.base_address = seg_start; return obj_di; @@ -1407,45 +1404,29 @@ pub const DebugInfo = struct { } }.callback)) { return error.DebugInfoNotFound; - } else |err| { - switch (err) { - error.Found => {}, - else => return error.DebugInfoNotFound, - } + } else |err| switch (err) { + error.Found => {}, + else => return error.DebugInfoNotFound, } if (self.address_map.getValue(ctx.base_address)) |obj_di| { return obj_di; } - const exe_file = if (ctx.name.len > 0) - try fs.openFileAbsolute(ctx.name, .{}) - else - try fs.openSelfExe(); - defer exe_file.close(); - - const exe_len = math.cast(usize, try exe_file.getEndPos()) catch - return error.DebugInfoTooLarge; - const exe_mmap = try os.mmap( - null, - exe_len, - os.PROT_READ, - os.MAP_SHARED, - exe_file.handle, - 0, - ); - errdefer os.munmap(exe_mmap); + const elf_path = if (ctx.name.len > 0) + ctx.name + else blk: { + var buf: [fs.MAX_PATH_BYTES]u8 = undefined; + break :blk try fs.selfExePath(&buf); + }; const obj_di = try self.allocator.create(ObjectDebugInfo); errdefer self.allocator.destroy(obj_di); try self.address_map.putNoClobber(ctx.base_address, obj_di); - obj_di.* = .{ - .dwarf = try openElfDebugInfo(self.allocator, exe_mmap), - .mapped_memory = exe_mmap, - .base_address = ctx.base_address, - }; + obj_di.* = try openElfDebugInfo(self.allocator, elf_path); + obj_di.base_address = ctx.base_address; return obj_di; } @@ -1454,7 +1435,7 @@ pub const DebugInfo = struct { pub const ObjectDebugInfo = switch (builtin.os) { .macosx, .ios, .watchos, .tvos => struct { base_address: usize, - mapped_memory: []u8, + mapped_memory: []const u8, symbols: []const MachoSymbol, strings: []const u8, ofiles: OFileTable, @@ -1480,7 +1461,7 @@ pub const ObjectDebugInfo = switch (builtin.os) { .linux, .freebsd => struct { base_address: usize, dwarf: DW.DwarfInfo, - mapped_memory: []u8, + mapped_memory: []const u8, }, else => DW.DwarfInfo, }; diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index 47727eeeeb..f174e38555 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -21,7 +21,7 @@ const PcRange = struct { const Func = struct { pc_range: ?PcRange, - name: ?[]u8, + name: ?[]const u8, }; const CompileUnit = struct { @@ -60,7 +60,7 @@ const FormValue = union(enum) { SecOffset: u64, Ref: u64, RefAddr: u64, - String: []u8, + String: []const u8, StrPtr: u64, }; @@ -124,7 +124,7 @@ const Die = struct { }; } - fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 { + fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]const u8 { const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; return switch (form_value.*) { FormValue.String => |value| value, @@ -740,7 +740,7 @@ pub const DwarfInfo = struct { } } - var include_directories = ArrayList([]u8).init(di.allocator()); + var include_directories = ArrayList([]const u8).init(di.allocator()); try include_directories.append(compile_unit_cwd); while (true) { const dir = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize)); @@ -861,7 +861,7 @@ pub const DwarfInfo = struct { return error.MissingDebugInfo; } - fn getString(di: *DwarfInfo, offset: u64) ![]u8 { + fn getString(di: *DwarfInfo, offset: u64) ![]const u8 { if (offset > di.debug_str.len) return error.InvalidDebugInfo; const casted_offset = math.cast(usize, offset) catch From a4d0d7f1de78a6d0b4993baeca0b8aa636ba9f33 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 20 Feb 2020 20:48:54 +0100 Subject: [PATCH 12/99] soldier on --- lib/std/debug.zig | 127 +++++++++++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 57 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 1e5a80d1fb..9b5f23001a 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -404,26 +404,30 @@ pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: us /// TODO resources https://github.com/ziglang/zig/issues/4353 fn printSourceAtAddressWindows( - di1: *DebugInfo, + debug_info: *DebugInfo, out_stream: var, - relocated_address: usize, + address: usize, tty_config: TTY.Config, ) !void { - const di = try di1.lookupByAddress(relocated_address); + const allocator = debug_info.allocator; - const allocator = getDebugInfoAllocator(); - const base_address = di.base_address; - const relative_address = relocated_address - base_address; + const module = debug_info.lookupByAddress(address) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => { + return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); + }, + else => return err, + }; + const relocated_address = address - module.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; + const mod_index = for (module.sect_contribs) |sect_contrib| { + if (sect_contrib.Section > module.coff.sections.len) continue; // Remember that SectionContribEntry.Section is 1-based. - coff_section = &di.coff.sections.toSlice()[sect_contrib.Section - 1]; + coff_section = &module.coff.sections.toSlice()[sect_contrib.Section - 1]; const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; const vaddr_end = vaddr_start + sect_contrib.Size; - if (relative_address >= vaddr_start and relative_address < vaddr_end) { + if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { break sect_contrib.ModuleIndex; } } else { @@ -431,8 +435,8 @@ fn printSourceAtAddressWindows( return printLineInfo(out_stream, null, relocated_address, "???", "???", tty_config, printLineFromFileAnyOs); }; - const mod = &di.modules[mod_index]; - try populateModule(di, mod); + const mod = &module.modules[mod_index]; + try populateModule(module, mod); const obj_basename = fs.path.basename(mod.obj_file_name); var symbol_i: usize = 0; @@ -445,7 +449,7 @@ fn printSourceAtAddressWindows( 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) { + if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { break mem.toSliceConst(u8, @ptrCast([*:0]u8, proc_sym) + @sizeOf(pdb.ProcSym)); } }, @@ -477,7 +481,7 @@ fn printSourceAtAddressWindows( 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) { + if (relocated_address >= frag_vaddr_start and relocated_address < frag_vaddr_end) { // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records) // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in, // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection. @@ -491,7 +495,8 @@ fn printSourceAtAddressWindows( const has_column = line_hdr.Flags.LF_HaveColumns; // All line entries are stored inside their line block by ascending start address. - // Heuristic: we want to find the last line entry that has a vaddr_start <= relative_address. + // Heuristic: we want to find the last line entry + // that has a vaddr_start <= relocated_address. // This is done with a simple linear search. var line_i: u32 = 0; while (line_i < block_hdr.NumLines) : (line_i += 1) { @@ -499,7 +504,7 @@ fn printSourceAtAddressWindows( line_index += @sizeOf(pdb.LineNumberEntry); const vaddr_start = frag_vaddr_start + line_num_entry.Offset; - if (relative_address < vaddr_start) { + if (relocated_address < vaddr_start) { break; } } @@ -509,8 +514,8 @@ fn printSourceAtAddressWindows( 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); + try module.pdb.string_table.seekTo(strtab_offset); + const source_file_name = try module.pdb.string_table.readNullTermString(allocator); const line_entry_idx = line_i - 1; @@ -696,24 +701,28 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach return null; } -fn printSourceAtAddressMacOs(di1: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { - const di = try di1.lookupByAddress(address); +fn printSourceAtAddressMacOs(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { + const module = debug_info.lookupByAddress(address) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => { + return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); + }, + else => return err, + }; - const base_addr = di.base_address; - const adjusted_addr = address - base_addr; - assert(adjusted_addr >= 0x100000000); + const relocated_address = address - module.base_address; + assert(relocated_address >= 0x100000000); - const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse { + const symbol = machoSearchSymbols(module.symbols, relocated_address) orelse { return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); }; - const symbol_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + symbol.nlist.n_strx)); + const symbol_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, module.strings.ptr + symbol.nlist.n_strx)); const compile_unit_name = if (symbol.ofile) |ofile| blk: { - const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx)); + const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, module.strings.ptr + ofile.n_strx)); break :blk fs.path.basename(ofile_path); } else "???"; - const line_info = getLineNumberInfoMacOs(di, symbol.*, adjusted_addr) catch |err| switch (err) { + const line_info = getLineNumberInfoMacOs(module, symbol.*, relocated_address) catch |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => null, else => return err, }; @@ -731,37 +740,41 @@ fn printSourceAtAddressMacOs(di1: *DebugInfo, out_stream: var, address: usize, t } pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { - // XXX Print as much as possible anyway - const module = try debug_info.lookupByAddress(address); + var symbol_name: []const u8 = ""; + var compile_unit_name: []const u8 = ""; + var line_info: ?LineInfo = null; - const reloc_address = address - module.base_address; + if (debug_info.lookupByAddress(address)) |module| { + // Translate the VA into an address into this object + const relocated_address = address - module.base_address; - if (module.dwarf.findCompileUnit(reloc_address) catch null) |compile_unit| { - const compile_unit_name = try compile_unit.die.getAttrString(&module.dwarf, DW.AT_name); - const symbol_name = module.dwarf.getSymbolName(reloc_address) orelse "???"; - const line_info = module.dwarf.getLineNumberInfo(compile_unit.*, reloc_address) catch |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => null, + if (module.dwarf.findCompileUnit(relocated_address)) |compile_unit| { + symbol_name = module.dwarf.getSymbolName(relocated_address) orelse "???"; + compile_unit_name = compile_unit.die.getAttrString(&module.dwarf, DW.AT_name) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => "???", + else => return err, + }; + line_info = module.dwarf.getLineNumberInfo(compile_unit.*, relocated_address) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => null, + else => return err, + }; + } else |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => {}, else => return err, - }; - defer if (line_info) |li| li.deinit(); - - return printLineInfo( - out_stream, - line_info, - address, - symbol_name, - compile_unit_name, - tty_config, - printLineFromFileAnyOs, - ); + } + } else |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => {}, + else => return err, } + defer if (line_info) |li| li.deinit(); + return printLineInfo( out_stream, - null, + line_info, address, - "???", - "???", + symbol_name, + compile_unit_name, tty_config, printLineFromFileAnyOs, ); @@ -1286,7 +1299,7 @@ pub const DebugInfo = struct { } } - return error.DebugInfoNotFound; + return error.MissingDebugInfo; } fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ObjectDebugInfo { @@ -1301,7 +1314,7 @@ pub const DebugInfo = struct { 0, &bytes_needed, ) == 0) - return error.DebugInfoNotFound; + return error.MissingDebugInfo; const needed_modules = bytes_needed / @sizeOf(windows.HMODULE); @@ -1314,7 +1327,7 @@ pub const DebugInfo = struct { try math.cast(windows.DWORD, modules.len * @sizeOf(windows.HMODULE)), &bytes_needed, ) == 0) - return error.DebugInfoNotFound; + return error.MissingDebugInfo; // There's an unavoidable TOCTOU problem here, the module list may have // changed between the two EnumProcessModules call. @@ -1330,7 +1343,7 @@ pub const DebugInfo = struct { &info, @sizeOf(@TypeOf(info)), ) == 0) - return error.DebugInfoNotFound; + return error.MissingDebugInfo; const seg_start = @ptrToInt(info.lpBaseOfDll); const seg_end = seg_start + info.SizeOfImage; @@ -1363,7 +1376,7 @@ pub const DebugInfo = struct { } } - return error.DebugInfoNotFound; + return error.MissingDebugInfo; } fn lookupModuleDl(self: *DebugInfo, address: usize) !*ObjectDebugInfo { @@ -1403,10 +1416,10 @@ pub const DebugInfo = struct { } } }.callback)) { - return error.DebugInfoNotFound; + return error.MissingDebugInfo; } else |err| switch (err) { error.Found => {}, - else => return error.DebugInfoNotFound, + else => return error.MissingDebugInfo, } if (self.address_map.getValue(ctx.base_address)) |obj_di| { From 8df4d7e4f445e8e9598b79bc495503d919c0d99a Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 21 Feb 2020 12:11:04 +0100 Subject: [PATCH 13/99] unsure --- lib/std/debug.zig | 75 +++++++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 9b5f23001a..27c53a1f37 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -740,41 +740,22 @@ fn printSourceAtAddressMacOs(debug_info: *DebugInfo, out_stream: var, address: u } pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { - var symbol_name: []const u8 = ""; - var compile_unit_name: []const u8 = ""; - var line_info: ?LineInfo = null; - - if (debug_info.lookupByAddress(address)) |module| { - // Translate the VA into an address into this object - const relocated_address = address - module.base_address; - - if (module.dwarf.findCompileUnit(relocated_address)) |compile_unit| { - symbol_name = module.dwarf.getSymbolName(relocated_address) orelse "???"; - compile_unit_name = compile_unit.die.getAttrString(&module.dwarf, DW.AT_name) catch |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => "???", - else => return err, - }; - line_info = module.dwarf.getLineNumberInfo(compile_unit.*, relocated_address) catch |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => null, - else => return err, - }; - } else |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => {}, - else => return err, - } - } else |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => {}, + const module = debug_info.lookupByAddress(address) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => { + return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); + }, else => return err, - } + }; - defer if (line_info) |li| li.deinit(); + const info = try module.getSymbolAtAddress(address); + defer if (info.line_info) |li| li.deinit(); return printLineInfo( out_stream, - line_info, + info.line_info, address, - symbol_name, - compile_unit_name, + info.symbol_name, + info.compile_unit_name, tty_config, printLineFromFileAnyOs, ); @@ -1445,6 +1426,12 @@ pub const DebugInfo = struct { } }; +const SymbolInfo = struct { + symbol_name: []const u8, + compile_unit_name: []const u8, + line_info: ?LineInfo, +}; + pub const ObjectDebugInfo = switch (builtin.os) { .macosx, .ios, .watchos, .tvos => struct { base_address: usize, @@ -1475,6 +1462,36 @@ pub const ObjectDebugInfo = switch (builtin.os) { base_address: usize, dwarf: DW.DwarfInfo, mapped_memory: []const u8, + + fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { + // Translate the VA into an address into this object + const relocated_address = address - self.base_address; + + if (self.dwarf.findCompileUnit(relocated_address)) |compile_unit| { + return SymbolInfo{ + .symbol_name = self.dwarf.getSymbolName(relocated_address) orelse "???", + .compile_unit_name = compile_unit.die.getAttrString(&self.dwarf, DW.AT_name) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => "???", + else => return err, + }, + .line_info = self.dwarf.getLineNumberInfo(compile_unit.*, relocated_address) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => null, + else => return err, + }, + }; + } else |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => { + return SymbolInfo{ + .symbol_name = "???", + .compile_unit_name = "???", + .line_info = null, + }; + }, + else => return err, + } + + unreachable; + } }, else => DW.DwarfInfo, }; From c060cae3ce1a47032b36bd295a3725760145e811 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 22 Feb 2020 11:51:45 +0100 Subject: [PATCH 14/99] unification: osx debug info --- lib/std/debug.zig | 376 ++++++++++++++++++++++------------------------ 1 file changed, 183 insertions(+), 193 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 27c53a1f37..53486d137c 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -396,9 +396,6 @@ pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: us if (builtin.os == .windows) { return noasync printSourceAtAddressWindows(debug_info, out_stream, address, tty_config); } - if (comptime std.Target.current.isDarwin()) { - return noasync printSourceAtAddressMacOs(debug_info, out_stream, address, tty_config); - } return noasync printSourceAtAddressPosix(debug_info, out_stream, address, tty_config); } @@ -411,7 +408,7 @@ fn printSourceAtAddressWindows( ) !void { const allocator = debug_info.allocator; - const module = debug_info.lookupByAddress(address) catch |err| switch (err) { + const module = debug_info.getModuleForAddress(address) catch |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => { return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); }, @@ -637,7 +634,7 @@ pub const TTY = struct { }; /// TODO resources https://github.com/ziglang/zig/issues/4353 -fn populateModule(di: *ObjectDebugInfo, mod: *Module) !void { +fn populateModule(di: *ModuleDebugInfo, mod: *Module) !void { if (mod.populated) return; const allocator = getDebugInfoAllocator(); @@ -701,46 +698,8 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach return null; } -fn printSourceAtAddressMacOs(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { - const module = debug_info.lookupByAddress(address) catch |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => { - return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); - }, - else => return err, - }; - - const relocated_address = address - module.base_address; - assert(relocated_address >= 0x100000000); - - const symbol = machoSearchSymbols(module.symbols, relocated_address) orelse { - return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); - }; - - const symbol_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, module.strings.ptr + symbol.nlist.n_strx)); - const compile_unit_name = if (symbol.ofile) |ofile| blk: { - const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, module.strings.ptr + ofile.n_strx)); - break :blk fs.path.basename(ofile_path); - } else "???"; - - const line_info = getLineNumberInfoMacOs(module, symbol.*, relocated_address) catch |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => null, - else => return err, - }; - defer if (line_info) |li| li.deinit(); - - try printLineInfo( - out_stream, - line_info, - address, - symbol_name, - compile_unit_name, - tty_config, - printLineFromFileAnyOs, - ); -} - pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { - const module = debug_info.lookupByAddress(address) catch |err| switch (err) { + const module = debug_info.getModuleForAddress(address) catch |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => { return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); }, @@ -748,7 +707,7 @@ pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, addres }; const info = try module.getSymbolAtAddress(address); - defer if (info.line_info) |li| li.deinit(); + defer info.deinit(); return printLineInfo( out_stream, @@ -833,14 +792,14 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { } /// TODO resources https://github.com/ziglang/zig/issues/4353 -fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ObjectDebugInfo { +fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo { const coff_file = try std.fs.openFileAbsoluteW(coff_file_path.ptr, .{}); errdefer coff_file.close(); const coff_obj = try allocator.create(coff.Coff); coff_obj.* = coff.Coff.init(allocator, coff_file); - var di = ObjectDebugInfo{ + var di = ModuleDebugInfo{ .base_address = undefined, .coff = coff_obj, .pdb = undefined, @@ -1012,7 +971,7 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 { } /// TODO resources https://github.com/ziglang/zig/issues/4353 -pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ObjectDebugInfo { +pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo { const mapped_mem = mapWholeFile(elf_file_path) catch |_| return error.InvalidDebugInfo; var seekable_stream = io.SliceSeekableInStream.init(mapped_mem); @@ -1047,7 +1006,7 @@ pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !O try DW.openDwarfDebugInfo(&di, allocator); - return ObjectDebugInfo{ + return ModuleDebugInfo{ .base_address = undefined, .dwarf = di, .mapped_memory = mapped_mem, @@ -1055,14 +1014,15 @@ pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !O } /// TODO resources https://github.com/ziglang/zig/issues/4353 -fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !ObjectDebugInfo { +fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !ModuleDebugInfo { const mapped_mem = mapWholeFile(macho_file_path) catch |_| return error.InvalidDebugInfo; const hdr = @ptrCast( *const macho.mach_header_64, @alignCast(@alignOf(macho.mach_header_64), mapped_mem.ptr), ); - assert(hdr.magic == std.macho.MH_MAGIC_64); + if (hdr.magic != macho.MH_MAGIC_64) + return error.InvalidDebugInfo; const hdr_base = @ptrCast([*]const u8, hdr); var ptr = hdr_base + @sizeOf(macho.mach_header_64); @@ -1078,7 +1038,7 @@ fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !O return error.MissingDebugInfo; }; const syms = @ptrCast([*]const macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms]; - const strings = @ptrCast([*]const u8, hdr_base + symtab.stroff)[0..symtab.strsize]; + const strings = @ptrCast([*]const u8, hdr_base + symtab.stroff)[0..symtab.strsize :0]; const symbols_buf = try allocator.alloc(MachoSymbol, syms.len); @@ -1130,10 +1090,10 @@ fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !O // This sort is so that we can binary search later. std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan); - return ObjectDebugInfo{ + return ModuleDebugInfo{ .base_address = undefined, .mapped_memory = mapped_mem, - .ofiles = ObjectDebugInfo.OFileTable.init(allocator), + .ofiles = ModuleDebugInfo.OFileTable.init(allocator), .symbols = symbols, .strings = strings, }; @@ -1206,12 +1166,12 @@ fn mapWholeFile(path: []const u8) ![]const u8 { pub const DebugInfo = struct { allocator: *mem.Allocator, - address_map: std.AutoHashMap(usize, *ObjectDebugInfo), + address_map: std.AutoHashMap(usize, *ModuleDebugInfo), pub fn init(allocator: *mem.Allocator) DebugInfo { return DebugInfo{ .allocator = allocator, - .address_map = std.AutoHashMap(usize, *ObjectDebugInfo).init(allocator), + .address_map = std.AutoHashMap(usize, *ModuleDebugInfo).init(allocator), }; } @@ -1220,17 +1180,16 @@ pub const DebugInfo = struct { self.address_map.deinit(); } - pub fn lookupByAddress(self: *DebugInfo, address: usize) !*ObjectDebugInfo { - const obj_di = if (comptime std.Target.current.isDarwin()) - try self.lookupModuleDyld(address) + pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo { + if (comptime std.Target.current.isDarwin()) + return self.lookupModuleDyld(address) else if (builtin.os == .windows) - try self.lookupModuleWin32(address) + return self.lookupModuleWin32(address) else - try self.lookupModuleDl(address); - return obj_di; + return self.lookupModuleDl(address); } - fn lookupModuleDyld(self: *DebugInfo, address: usize) !*ObjectDebugInfo { + fn lookupModuleDyld(self: *DebugInfo, address: usize) !*ModuleDebugInfo { const image_count = std.c._dyld_image_count(); var i: u32 = 0; @@ -1266,7 +1225,7 @@ pub const DebugInfo = struct { return obj_di; } - const obj_di = try self.allocator.create(ObjectDebugInfo); + const obj_di = try self.allocator.create(ModuleDebugInfo); errdefer self.allocator.destroy(obj_di); try self.address_map.putNoClobber(base_address, obj_di); @@ -1283,7 +1242,7 @@ pub const DebugInfo = struct { return error.MissingDebugInfo; } - fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ObjectDebugInfo { + fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo { const process_handle = windows.kernel32.GetCurrentProcess(); // Find how many modules are actually loaded @@ -1345,7 +1304,7 @@ pub const DebugInfo = struct { ); assert(len > 0); - const obj_di = try self.allocator.create(ObjectDebugInfo); + const obj_di = try self.allocator.create(ModuleDebugInfo); errdefer self.allocator.destroy(obj_di); try self.address_map.putNoClobber(seg_start, obj_di); @@ -1360,7 +1319,7 @@ pub const DebugInfo = struct { return error.MissingDebugInfo; } - fn lookupModuleDl(self: *DebugInfo, address: usize) !*ObjectDebugInfo { + fn lookupModuleDl(self: *DebugInfo, address: usize) !*ModuleDebugInfo { var ctx: struct { // Input address: usize, @@ -1414,7 +1373,7 @@ pub const DebugInfo = struct { break :blk try fs.selfExePath(&buf); }; - const obj_di = try self.allocator.create(ObjectDebugInfo); + const obj_di = try self.allocator.create(ModuleDebugInfo); errdefer self.allocator.destroy(obj_di); try self.address_map.putNoClobber(ctx.base_address, obj_di); @@ -1427,29 +1386,173 @@ pub const DebugInfo = struct { }; const SymbolInfo = struct { - symbol_name: []const u8, - compile_unit_name: []const u8, - line_info: ?LineInfo, + symbol_name: []const u8 = "???", + compile_unit_name: []const u8 = "???", + line_info: ?LineInfo = null, + + fn deinit(self: @This()) void { + if (self.line_info) |li| { + li.deinit(); + } + } }; -pub const ObjectDebugInfo = switch (builtin.os) { +pub const ModuleDebugInfo = switch (builtin.os) { .macosx, .ios, .watchos, .tvos => struct { base_address: usize, mapped_memory: []const u8, symbols: []const MachoSymbol, - strings: []const u8, + strings: [:0]const u8, ofiles: OFileTable, - const OFileTable = std.HashMap( - *const macho.nlist_64, - DW.DwarfInfo, - std.hash_map.getHashPtrAddrFn(*const macho.nlist_64), - std.hash_map.getTrivialEqlFn(*const macho.nlist_64), - ); + const OFileTable = std.StringHashMap(DW.DwarfInfo); pub fn allocator(self: @This()) *mem.Allocator { return self.ofiles.allocator; } + + fn loadOFile(self: *@This(), o_file_path: []const u8) !DW.DwarfInfo { + const mapped_mem = mapWholeFile(o_file_path) catch |_| return error.InvalidDebugInfo; + + const hdr = @ptrCast( + *const macho.mach_header_64, + @alignCast(@alignOf(macho.mach_header_64), mapped_mem.ptr), + ); + if (hdr.magic != std.macho.MH_MAGIC_64) + return error.InvalidDebugInfo; + + const hdr_base = @ptrCast([*]const u8, hdr); + var ptr = hdr_base + @sizeOf(macho.mach_header_64); + var ncmd: u32 = hdr.ncmds; + const segcmd = while (ncmd != 0) : (ncmd -= 1) { + const lc = @ptrCast(*const std.macho.load_command, ptr); + switch (lc.cmd) { + std.macho.LC_SEGMENT_64 => { + break @ptrCast( + *const std.macho.segment_command_64, + @alignCast(@alignOf(std.macho.segment_command_64), ptr), + ); + }, + else => {}, + } + ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize); + } else { + return error.MissingDebugInfo; + }; + + var opt_debug_line: ?*const macho.section_64 = null; + var opt_debug_info: ?*const macho.section_64 = null; + var opt_debug_abbrev: ?*const macho.section_64 = null; + var opt_debug_str: ?*const macho.section_64 = null; + var opt_debug_ranges: ?*const macho.section_64 = null; + + const sections = @ptrCast( + [*]const macho.section_64, + @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)), + )[0..segcmd.nsects]; + for (sections) |*sect| { + // The section name may not exceed 16 chars and a trailing null may + // not be present + const name = if (mem.indexOfScalar(u8, sect.sectname[0..], 0)) |last| + sect.sectname[0..last] + else + sect.sectname[0..]; + + if (mem.eql(u8, name, "__debug_line")) { + opt_debug_line = sect; + } else if (mem.eql(u8, name, "__debug_info")) { + opt_debug_info = sect; + } else if (mem.eql(u8, name, "__debug_abbrev")) { + opt_debug_abbrev = sect; + } else if (mem.eql(u8, name, "__debug_str")) { + opt_debug_str = sect; + } else if (mem.eql(u8, name, "__debug_ranges")) { + opt_debug_ranges = sect; + } + } + + const debug_line = opt_debug_line orelse + return error.MissingDebugInfo; + const debug_info = opt_debug_info orelse + return error.MissingDebugInfo; + const debug_str = opt_debug_str orelse + return error.MissingDebugInfo; + const debug_abbrev = opt_debug_abbrev orelse + return error.MissingDebugInfo; + + var di = DW.DwarfInfo{ + .endian = .Little, + .debug_info = try chopSlice(mapped_mem, debug_info.offset, debug_info.size), + .debug_abbrev = try chopSlice(mapped_mem, debug_abbrev.offset, debug_abbrev.size), + .debug_str = try chopSlice(mapped_mem, debug_str.offset, debug_str.size), + .debug_line = try chopSlice(mapped_mem, debug_line.offset, debug_line.size), + .debug_ranges = if (opt_debug_ranges) |debug_ranges| + try chopSlice(mapped_mem, debug_ranges.offset, debug_ranges.size) + else + null, + }; + + try DW.openDwarfDebugInfo(&di, self.allocator()); + + // Add the debug info to the cache + try self.ofiles.putNoClobber(o_file_path, di); + + return di; + } + + fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { + // Translate the VA into an address into this object + const relocated_address = address - self.base_address; + assert(relocated_address >= 0x100000000); + + // Find the .o file where this symbol is defined + const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse + return SymbolInfo{}; + + // XXX: Return the symbol name + if (symbol.ofile == null) + return SymbolInfo{}; + + assert(symbol.ofile.?.n_strx < self.strings.len); + const o_file_path = mem.toSliceConst(u8, self.strings.ptr + symbol.ofile.?.n_strx); + + warn("Loading .o file: {s}\n", .{o_file_path}); + + // Check if its debug infos are already in the cache + var o_file_di = self.ofiles.getValue(o_file_path) orelse + (self.loadOFile(o_file_path) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => { + // XXX: Return the symbol name + return SymbolInfo{}; + }, + else => return err, + }); + + // Translate again the address, this time into an address inside the + // .o file + const relocated_address_o = relocated_address - symbol.reloc; + + if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| { + return SymbolInfo{ + .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???", + .compile_unit_name = compile_unit.die.getAttrString(&o_file_di, DW.AT_name) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => "???", + else => return err, + }, + .line_info = o_file_di.getLineNumberInfo(compile_unit.*, relocated_address_o) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => null, + else => return err, + }, + }; + } else |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => { + return SymbolInfo{}; + }, + else => return err, + } + + unreachable; + } }, .uefi, .windows => struct { base_address: usize, @@ -1481,11 +1584,7 @@ pub const ObjectDebugInfo = switch (builtin.os) { }; } else |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => { - return SymbolInfo{ - .symbol_name = "???", - .compile_unit_name = "???", - .line_info = null, - }; + return SymbolInfo{}; }, else => return err, } @@ -1496,115 +1595,6 @@ pub const ObjectDebugInfo = switch (builtin.os) { else => DW.DwarfInfo, }; -/// TODO resources https://github.com/ziglang/zig/issues/4353 -fn getLineNumberInfoMacOs(di: *ObjectDebugInfo, symbol: MachoSymbol, address: usize) !LineInfo { - const ofile = symbol.ofile orelse return error.MissingDebugInfo; - const gop = try di.ofiles.getOrPut(ofile); - const dwarf_info = if (gop.found_existing) &gop.kv.value else blk: { - errdefer _ = di.ofiles.remove(ofile); - const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx)); - - var exe_file = try std.fs.openFileAbsoluteC(ofile_path, .{}); - errdefer exe_file.close(); - - const exe_len = math.cast(usize, try exe_file.getEndPos()) catch - return error.DebugInfoTooLarge; - const exe_mmap = try os.mmap( - null, - exe_len, - os.PROT_READ, - os.MAP_SHARED, - exe_file.handle, - 0, - ); - errdefer os.munmap(exe_mmap); - - const hdr = @ptrCast( - *const macho.mach_header_64, - @alignCast(@alignOf(macho.mach_header_64), exe_mmap.ptr), - ); - if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo; - - const hdr_base = @ptrCast([*]const u8, hdr); - var ptr = hdr_base + @sizeOf(macho.mach_header_64); - var ncmd: u32 = hdr.ncmds; - const segcmd = while (ncmd != 0) : (ncmd -= 1) { - const lc = @ptrCast(*const std.macho.load_command, ptr); - switch (lc.cmd) { - std.macho.LC_SEGMENT_64 => { - break @ptrCast( - *const std.macho.segment_command_64, - @alignCast(@alignOf(std.macho.segment_command_64), ptr), - ); - }, - else => {}, - } - ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize); - } else { - return error.MissingDebugInfo; - }; - - var opt_debug_line: ?*const macho.section_64 = null; - var opt_debug_info: ?*const macho.section_64 = null; - var opt_debug_abbrev: ?*const macho.section_64 = null; - var opt_debug_str: ?*const macho.section_64 = null; - var opt_debug_ranges: ?*const macho.section_64 = null; - - const sections = @ptrCast( - [*]const macho.section_64, - @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)), - )[0..segcmd.nsects]; - for (sections) |*sect| { - // The section name may not exceed 16 chars and a trailing null may - // not be present - const name = if (mem.indexOfScalar(u8, sect.sectname[0..], 0)) |last| - sect.sectname[0..last] - else - sect.sectname[0..]; - - if (mem.eql(u8, name, "__debug_line")) { - opt_debug_line = sect; - } else if (mem.eql(u8, name, "__debug_info")) { - opt_debug_info = sect; - } else if (mem.eql(u8, name, "__debug_abbrev")) { - opt_debug_abbrev = sect; - } else if (mem.eql(u8, name, "__debug_str")) { - opt_debug_str = sect; - } else if (mem.eql(u8, name, "__debug_ranges")) { - opt_debug_ranges = sect; - } - } - - var debug_line = opt_debug_line orelse - return error.MissingDebugInfo; - var debug_info = opt_debug_info orelse - return error.MissingDebugInfo; - var debug_str = opt_debug_str orelse - return error.MissingDebugInfo; - var debug_abbrev = opt_debug_abbrev orelse - return error.MissingDebugInfo; - - gop.kv.value = DW.DwarfInfo{ - .endian = .Little, - .debug_info = try chopSlice(exe_mmap, debug_info.offset, debug_info.size), - .debug_abbrev = try chopSlice(exe_mmap, debug_abbrev.offset, debug_abbrev.size), - .debug_str = try chopSlice(exe_mmap, debug_str.offset, debug_str.size), - .debug_line = try chopSlice(exe_mmap, debug_line.offset, debug_line.size), - .debug_ranges = if (opt_debug_ranges) |debug_ranges| - try chopSlice(exe_mmap, debug_ranges.offset, debug_ranges.size) - else - null, - }; - try DW.openDwarfDebugInfo(&gop.kv.value, di.allocator()); - - break :blk &gop.kv.value; - }; - - const o_file_address = address - symbol.reloc; - const compile_unit = try dwarf_info.findCompileUnit(o_file_address); - return dwarf_info.getLineNumberInfo(compile_unit.*, o_file_address); -} - /// TODO multithreaded awareness var debug_info_allocator: ?*mem.Allocator = null; var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined; From 41bca1ce760f415df5bc89acb0f609659e9bdad8 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 22 Feb 2020 12:05:50 +0100 Subject: [PATCH 15/99] unification: windows debug info --- lib/std/debug.zig | 160 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 155 insertions(+), 5 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 53486d137c..6b71a14fa6 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -393,9 +393,9 @@ pub fn writeCurrentStackTraceWindows( /// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented, /// make this `noasync fn` and remove the individual noasync calls. pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { - if (builtin.os == .windows) { - return noasync printSourceAtAddressWindows(debug_info, out_stream, address, tty_config); - } + // if (builtin.os == .windows) { + // return noasync printSourceAtAddressWindows(debug_info, out_stream, address, tty_config); + // } return noasync printSourceAtAddressPosix(debug_info, out_stream, address, tty_config); } @@ -1516,8 +1516,6 @@ pub const ModuleDebugInfo = switch (builtin.os) { assert(symbol.ofile.?.n_strx < self.strings.len); const o_file_path = mem.toSliceConst(u8, self.strings.ptr + symbol.ofile.?.n_strx); - warn("Loading .o file: {s}\n", .{o_file_path}); - // Check if its debug infos are already in the cache var o_file_di = self.ofiles.getValue(o_file_path) orelse (self.loadOFile(o_file_path) catch |err| switch (err) { @@ -1560,6 +1558,158 @@ pub const ModuleDebugInfo = switch (builtin.os) { coff: *coff.Coff, sect_contribs: []pdb.SectionContribEntry, modules: []Module, + + pub fn allocator(self: @This()) *mem.Allocator { + return self.coff.allocator; + } + + fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { + // Translate the VA into an address into this object + const relocated_address = address - self.base_address; + + var coff_section: *coff.Section = undefined; + const mod_index = for (self.sect_contribs) |sect_contrib| { + if (sect_contrib.Section > self.coff.sections.len) continue; + // Remember that SectionContribEntry.Section is 1-based. + coff_section = &self.coff.sections.toSlice()[sect_contrib.Section - 1]; + + const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; + const vaddr_end = vaddr_start + sect_contrib.Size; + if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { + break sect_contrib.ModuleIndex; + } + } else { + // we have no information to add to the address + return SymbolInfo{}; + }; + + const mod = &self.modules[mod_index]; + try populateModule(self, mod); + const obj_basename = fs.path.basename(mod.obj_file_name); + + var symbol_i: usize = 0; + const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) { + const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); + if (prefix.RecordLen < 2) + return error.InvalidDebugInfo; + switch (prefix.RecordKind) { + .S_LPROC32, .S_GPROC32 => { + const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); + const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; + const vaddr_end = vaddr_start + proc_sym.CodeSize; + if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { + break mem.toSliceConst(u8, @ptrCast([*:0]u8, proc_sym) + @sizeOf(pdb.ProcSym)); + } + }, + else => {}, + } + symbol_i += prefix.RecordLen + @sizeOf(u16); + if (symbol_i > mod.symbols.len) + return error.InvalidDebugInfo; + } else "???"; + + const subsect_info = mod.subsect_info; + + var sect_offset: usize = 0; + var skip_len: usize = undefined; + const opt_line_info = subsections: { + const checksum_offset = mod.checksum_offset orelse break :subsections null; + while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { + const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]); + skip_len = subsect_hdr.Length; + sect_offset += @sizeOf(pdb.DebugSubsectionHeader); + + switch (subsect_hdr.Kind) { + pdb.DebugSubsectionKind.Lines => { + var line_index = sect_offset; + + const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]); + if (line_hdr.RelocSegment == 0) + return error.MissingDebugInfo; + line_index += @sizeOf(pdb.LineFragmentHeader); + const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; + const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; + + if (relocated_address >= frag_vaddr_start and relocated_address < frag_vaddr_end) { + // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records) + // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in, + // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection. + const subsection_end_index = sect_offset + subsect_hdr.Length; + + while (line_index < subsection_end_index) { + const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]); + line_index += @sizeOf(pdb.LineBlockFragmentHeader); + const start_line_index = line_index; + + const has_column = line_hdr.Flags.LF_HaveColumns; + + // All line entries are stored inside their line block by ascending start address. + // Heuristic: we want to find the last line entry + // that has a vaddr_start <= relocated_address. + // This is done with a simple linear search. + var line_i: u32 = 0; + while (line_i < block_hdr.NumLines) : (line_i += 1) { + const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]); + line_index += @sizeOf(pdb.LineNumberEntry); + + const vaddr_start = frag_vaddr_start + line_num_entry.Offset; + if (relocated_address < vaddr_start) { + break; + } + } + + // line_i == 0 would mean that no matching LineNumberEntry was found. + if (line_i > 0) { + const subsect_index = checksum_offset + block_hdr.NameIndex; + const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]); + const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset; + try self.pdb.string_table.seekTo(strtab_offset); + const source_file_name = try self.pdb.string_table.readNullTermString(self.allocator()); + + const line_entry_idx = line_i - 1; + + const column = if (has_column) blk: { + const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines; + const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx; + const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]); + break :blk col_num_entry.StartColumn; + } else 0; + + const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry); + const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]); + const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags); + + break :subsections LineInfo{ + .allocator = self.allocator(), + .file_name = source_file_name, + .line = flags.Start, + .column = column, + }; + } + } + + // Checking that we are not reading garbage after the (possibly) multiple block fragments. + if (line_index != subsection_end_index) { + return error.InvalidDebugInfo; + } + } + }, + else => {}, + } + + if (sect_offset > subsect_info.len) + return error.InvalidDebugInfo; + } else { + break :subsections null; + } + }; + + return SymbolInfo{ + .symbol_name = symbol_name, + .compile_unit_name = obj_basename, + .line_info = opt_line_info, + }; + } }, .linux, .freebsd => struct { base_address: usize, From 495e894225d3bb5695681be87ed0cd1b7f9825a2 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 22 Feb 2020 12:44:21 +0100 Subject: [PATCH 16/99] delete extra code, more forgiveness --- lib/std/debug.zig | 229 ++++++++-------------------------------------- lib/std/macho.zig | 11 +++ 2 files changed, 48 insertions(+), 192 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 6b71a14fa6..cce3cd7917 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -390,179 +390,6 @@ pub fn writeCurrentStackTraceWindows( } } -/// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented, -/// make this `noasync fn` and remove the individual noasync calls. -pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { - // if (builtin.os == .windows) { - // return noasync printSourceAtAddressWindows(debug_info, out_stream, address, tty_config); - // } - return noasync printSourceAtAddressPosix(debug_info, out_stream, address, tty_config); -} - -/// TODO resources https://github.com/ziglang/zig/issues/4353 -fn printSourceAtAddressWindows( - debug_info: *DebugInfo, - out_stream: var, - address: usize, - tty_config: TTY.Config, -) !void { - const allocator = debug_info.allocator; - - const module = debug_info.getModuleForAddress(address) catch |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => { - return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); - }, - else => return err, - }; - const relocated_address = address - module.base_address; - - var coff_section: *coff.Section = undefined; - const mod_index = for (module.sect_contribs) |sect_contrib| { - if (sect_contrib.Section > module.coff.sections.len) continue; - // Remember that SectionContribEntry.Section is 1-based. - coff_section = &module.coff.sections.toSlice()[sect_contrib.Section - 1]; - - const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; - const vaddr_end = vaddr_start + sect_contrib.Size; - if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { - break sect_contrib.ModuleIndex; - } - } else { - // we have no information to add to the address - return printLineInfo(out_stream, null, relocated_address, "???", "???", tty_config, printLineFromFileAnyOs); - }; - - const mod = &module.modules[mod_index]; - try populateModule(module, mod); - const obj_basename = fs.path.basename(mod.obj_file_name); - - var symbol_i: usize = 0; - const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) { - const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); - if (prefix.RecordLen < 2) - return error.InvalidDebugInfo; - switch (prefix.RecordKind) { - .S_LPROC32, .S_GPROC32 => { - const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); - const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; - const vaddr_end = vaddr_start + proc_sym.CodeSize; - if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { - break mem.toSliceConst(u8, @ptrCast([*:0]u8, proc_sym) + @sizeOf(pdb.ProcSym)); - } - }, - else => {}, - } - symbol_i += prefix.RecordLen + @sizeOf(u16); - if (symbol_i > mod.symbols.len) - return error.InvalidDebugInfo; - } else "???"; - - const subsect_info = mod.subsect_info; - - var sect_offset: usize = 0; - var skip_len: usize = undefined; - const opt_line_info = subsections: { - const checksum_offset = mod.checksum_offset orelse break :subsections null; - while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { - const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]); - skip_len = subsect_hdr.Length; - sect_offset += @sizeOf(pdb.DebugSubsectionHeader); - - switch (subsect_hdr.Kind) { - pdb.DebugSubsectionKind.Lines => { - var line_index = sect_offset; - - const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]); - if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; - line_index += @sizeOf(pdb.LineFragmentHeader); - const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; - const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; - - if (relocated_address >= frag_vaddr_start and relocated_address < frag_vaddr_end) { - // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records) - // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in, - // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection. - const subsection_end_index = sect_offset + subsect_hdr.Length; - - while (line_index < subsection_end_index) { - const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]); - line_index += @sizeOf(pdb.LineBlockFragmentHeader); - const start_line_index = line_index; - - const has_column = line_hdr.Flags.LF_HaveColumns; - - // All line entries are stored inside their line block by ascending start address. - // Heuristic: we want to find the last line entry - // that has a vaddr_start <= relocated_address. - // This is done with a simple linear search. - var line_i: u32 = 0; - while (line_i < block_hdr.NumLines) : (line_i += 1) { - const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]); - line_index += @sizeOf(pdb.LineNumberEntry); - - const vaddr_start = frag_vaddr_start + line_num_entry.Offset; - if (relocated_address < vaddr_start) { - break; - } - } - - // line_i == 0 would mean that no matching LineNumberEntry was found. - if (line_i > 0) { - const subsect_index = checksum_offset + block_hdr.NameIndex; - const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]); - const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset; - try module.pdb.string_table.seekTo(strtab_offset); - const source_file_name = try module.pdb.string_table.readNullTermString(allocator); - - const line_entry_idx = line_i - 1; - - const column = if (has_column) blk: { - const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines; - const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx; - const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]); - break :blk col_num_entry.StartColumn; - } else 0; - - const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry); - const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]); - const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags); - - break :subsections LineInfo{ - .allocator = allocator, - .file_name = source_file_name, - .line = flags.Start, - .column = column, - }; - } - } - - // Checking that we are not reading garbage after the (possibly) multiple block fragments. - if (line_index != subsection_end_index) { - return error.InvalidDebugInfo; - } - } - }, - else => {}, - } - - if (sect_offset > subsect_info.len) - return error.InvalidDebugInfo; - } else { - break :subsections null; - } - }; - - try printLineInfo( - out_stream, - opt_line_info, - relocated_address, - symbol_name, - obj_basename, - tty_config, - printLineFromFileAnyOs, - ); -} - pub const TTY = struct { pub const Color = enum { Red, @@ -698,23 +525,32 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach return null; } -pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { +/// TODO resources https://github.com/ziglang/zig/issues/4353 +pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { const module = debug_info.getModuleForAddress(address) catch |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => { - return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); + return printLineInfo( + out_stream, + null, + address, + "???", + "???", + tty_config, + printLineFromFileAnyOs, + ); }, else => return err, }; - const info = try module.getSymbolAtAddress(address); - defer info.deinit(); + const symbol_info = try module.getSymbolAtAddress(address); + defer symbol_info.deinit(); return printLineInfo( out_stream, - info.line_info, + symbol_info.line_info, address, - info.symbol_name, - info.compile_unit_name, + symbol_info.symbol_name, + symbol_info.compile_unit_name, tty_config, printLineFromFileAnyOs, ); @@ -972,7 +808,7 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 { /// TODO resources https://github.com/ziglang/zig/issues/4353 pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo { - const mapped_mem = mapWholeFile(elf_file_path) catch |_| return error.InvalidDebugInfo; + const mapped_mem = try mapWholeFile(elf_file_path); var seekable_stream = io.SliceSeekableInStream.init(mapped_mem); var efile = try elf.Elf.openStream( @@ -1015,7 +851,7 @@ pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !M /// TODO resources https://github.com/ziglang/zig/issues/4353 fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !ModuleDebugInfo { - const mapped_mem = mapWholeFile(macho_file_path) catch |_| return error.InvalidDebugInfo; + const mapped_mem = try mapWholeFile(macho_file_path); const hdr = @ptrCast( *const macho.mach_header_64, @@ -1228,12 +1064,15 @@ pub const DebugInfo = struct { const obj_di = try self.allocator.create(ModuleDebugInfo); errdefer self.allocator.destroy(obj_di); - try self.address_map.putNoClobber(base_address, obj_di); - const macho_path = mem.toSliceConst(u8, std.c._dyld_get_image_name(i)); - obj_di.* = try openMachODebugInfo(self.allocator, macho_path); + obj_di.* = openMachODebugInfo(self.allocator, macho_path) catch |err| switch (err) { + error.FileNotFound => return error.MissingDebugInfo, + else => return err, + }; obj_di.base_address = base_address; + try self.address_map.putNoClobber(base_address, obj_di); + return obj_di; } } @@ -1307,11 +1146,14 @@ pub const DebugInfo = struct { const obj_di = try self.allocator.create(ModuleDebugInfo); errdefer self.allocator.destroy(obj_di); - try self.address_map.putNoClobber(seg_start, obj_di); - - obj_di.* = try openCoffDebugInfo(self.allocator, name_buffer[0..:0]); + obj_di.* = openCoffDebugInfo(self.allocator, name_buffer[0..:0]) catch |err| switch (err) { + error.FileNotFound => return error.MissingDebugInfo, + else => return err, + }; obj_di.base_address = seg_start; + try self.address_map.putNoClobber(seg_start, obj_di); + return obj_di; } } @@ -1376,11 +1218,14 @@ pub const DebugInfo = struct { const obj_di = try self.allocator.create(ModuleDebugInfo); errdefer self.allocator.destroy(obj_di); - try self.address_map.putNoClobber(ctx.base_address, obj_di); - - obj_di.* = try openElfDebugInfo(self.allocator, elf_path); + obj_di.* = openElfDebugInfo(self.allocator, elf_path) catch |err| switch (err) { + error.FileNotFound => return error.MissingDebugInfo, + else => return err, + }; obj_di.base_address = ctx.base_address; + try self.address_map.putNoClobber(ctx.base_address, obj_di); + return obj_di; } }; @@ -1412,7 +1257,7 @@ pub const ModuleDebugInfo = switch (builtin.os) { } fn loadOFile(self: *@This(), o_file_path: []const u8) !DW.DwarfInfo { - const mapped_mem = mapWholeFile(o_file_path) catch |_| return error.InvalidDebugInfo; + const mapped_mem = try mapWholeFile(o_file_path); const hdr = @ptrCast( *const macho.mach_header_64, diff --git a/lib/std/macho.zig b/lib/std/macho.zig index e1bbd755c6..a499a93675 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -24,6 +24,17 @@ pub const load_command = extern struct { cmdsize: u32, }; +pub const uuid_command = extern struct { + /// LC_UUID + cmd: u32, + + /// sizeof(struct uuid_command) + cmdsize: u32, + + /// the 128-bit uuid + uuid: [16]u8, +}; + /// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD /// "stab" style symbol table information as described in the header files /// and . From 08047cd6d710603a5391c9e88a7369f5dcfa196f Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 22 Feb 2020 18:02:55 +0100 Subject: [PATCH 17/99] correct test expectations --- lib/std/debug.zig | 4 ++-- test/stack_traces.zig | 37 +++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index cce3cd7917..a9f9819a25 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -493,7 +493,7 @@ fn populateModule(di: *ModuleDebugInfo, mod: *Module) !void { sect_offset += @sizeOf(pdb.DebugSubsectionHeader); switch (subsect_hdr.Kind) { - pdb.DebugSubsectionKind.FileChecksums => { + .FileChecksums => { mod.checksum_offset = sect_offset; break; }, @@ -1465,7 +1465,7 @@ pub const ModuleDebugInfo = switch (builtin.os) { sect_offset += @sizeOf(pdb.DebugSubsectionHeader); switch (subsect_hdr.Kind) { - pdb.DebugSubsectionKind.Lines => { + .Lines => { var line_index = sect_offset; const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]); diff --git a/test/stack_traces.zig b/test/stack_traces.zig index 81e074f01e..fd4ff69964 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -3,6 +3,7 @@ const std = @import("std"); const os = std.os; const tests = @import("tests.zig"); +// zig fmt: off pub fn addCases(cases: *tests.StackTracesContext) void { const source_return = \\const std = @import("std"); @@ -41,7 +42,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ try foo(); \\} ; - // zig fmt: off + switch (builtin.os) { .freebsd => { cases.addCase( @@ -264,14 +265,14 @@ pub fn addCases(cases: *tests.StackTracesContext) void { [_][]const u8{ // debug \\error: TheSkyIsFalling - \\source.zig:4:5: [address] in _main.0 (test.o) + \\source.zig:4:5: [address] in main (test) \\ return error.TheSkyIsFalling; \\ ^ \\ , // release-safe \\error: TheSkyIsFalling - \\source.zig:4:5: [address] in _main (test.o) + \\source.zig:4:5: [address] in std.start.main (test) \\ return error.TheSkyIsFalling; \\ ^ \\ @@ -291,20 +292,20 @@ pub fn addCases(cases: *tests.StackTracesContext) void { [_][]const u8{ // debug \\error: TheSkyIsFalling - \\source.zig:4:5: [address] in _foo (test.o) + \\source.zig:4:5: [address] in foo (test) \\ return error.TheSkyIsFalling; \\ ^ - \\source.zig:8:5: [address] in _main.0 (test.o) + \\source.zig:8:5: [address] in main (test) \\ try foo(); \\ ^ \\ , // release-safe \\error: TheSkyIsFalling - \\source.zig:4:5: [address] in _main (test.o) + \\source.zig:4:5: [address] in std.start.main (test) \\ return error.TheSkyIsFalling; \\ ^ - \\source.zig:8:5: [address] in _main (test.o) + \\source.zig:8:5: [address] in std.start.main (test) \\ try foo(); \\ ^ \\ @@ -324,32 +325,32 @@ pub fn addCases(cases: *tests.StackTracesContext) void { [_][]const u8{ // debug \\error: TheSkyIsFalling - \\source.zig:12:5: [address] in _make_error (test.o) + \\source.zig:12:5: [address] in make_error (test) \\ return error.TheSkyIsFalling; \\ ^ - \\source.zig:8:5: [address] in _bar (test.o) + \\source.zig:8:5: [address] in bar (test) \\ return make_error(); \\ ^ - \\source.zig:4:5: [address] in _foo (test.o) + \\source.zig:4:5: [address] in foo (test) \\ try bar(); \\ ^ - \\source.zig:16:5: [address] in _main.0 (test.o) + \\source.zig:16:5: [address] in main (test) \\ try foo(); \\ ^ \\ , // release-safe \\error: TheSkyIsFalling - \\source.zig:12:5: [address] in _main (test.o) + \\source.zig:12:5: [address] in std.start.main (test) \\ return error.TheSkyIsFalling; \\ ^ - \\source.zig:8:5: [address] in _main (test.o) + \\source.zig:8:5: [address] in std.start.main (test) \\ return make_error(); \\ ^ - \\source.zig:4:5: [address] in _main (test.o) + \\source.zig:4:5: [address] in std.start.main (test) \\ try bar(); \\ ^ - \\source.zig:16:5: [address] in _main (test.o) + \\source.zig:16:5: [address] in std.start.main (test) \\ try foo(); \\ ^ \\ @@ -393,7 +394,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { source_try_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in foo (test.obj) \\ return error.TheSkyIsFalling; \\ ^ @@ -419,7 +420,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { source_try_try_return_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:12:5: [address] in make_error (test.obj) \\ return error.TheSkyIsFalling; \\ ^ @@ -449,5 +450,5 @@ pub fn addCases(cases: *tests.StackTracesContext) void { }, else => {}, } - // zig fmt: off } +// zig fmt: off From b46efcde82436e73c73dab132f73aeff98673894 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 25 Feb 2020 12:22:21 +0100 Subject: [PATCH 18/99] ir: Fix sizeOf comparison with ptr to zst Closes #4536 --- src/analyze.cpp | 5 ++-- test/stage1/behavior/sizeof_and_typeof.zig | 34 ++++++++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 95b2c77129..5ee5f44643 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1132,10 +1132,9 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent if (type_val->special != ConstValSpecialLazy) { assert(type_val->special == ConstValSpecialStatic); if ((type_val->data.x_type->id == ZigTypeIdStruct && - type_val->data.x_type->data.structure.resolve_loop_flag_zero_bits) || + type_val->data.x_type->data.structure.resolve_loop_flag_zero_bits) || (type_val->data.x_type->id == ZigTypeIdUnion && - type_val->data.x_type->data.unionation.resolve_loop_flag_zero_bits) || - type_val->data.x_type->id == ZigTypeIdPointer) + type_val->data.x_type->data.unionation.resolve_loop_flag_zero_bits)) { // Does a struct/union which contains a pointer field to itself have bits? Yes. *is_zero_bits = false; diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig index a738dbbbf9..e3b62b15cc 100644 --- a/test/stage1/behavior/sizeof_and_typeof.zig +++ b/test/stage1/behavior/sizeof_and_typeof.zig @@ -1,5 +1,7 @@ -const builtin = @import("builtin"); -const expect = @import("std").testing.expect; +const std = @import("std"); +const builtin = std.builtin; +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; test "@sizeOf and @TypeOf" { const y: @TypeOf(x) = 120; @@ -135,3 +137,31 @@ test "@bitSizeOf" { a: u2 }) == 2); } + +test "@sizeOf comparison against zero" { + const S0 = struct { + f: *@This(), + }; + const U0 = union { + f: *@This(), + }; + const S = struct { + fn doTheTest(comptime T: type, comptime result: bool) void { + expectEqual(result, @sizeOf(T) > 0); + } + }; + // Zero-sized type + S.doTheTest(u0, false); + S.doTheTest(*u0, false); + // Non byte-sized type + S.doTheTest(u1, true); + S.doTheTest(*u1, true); + // Regular type + S.doTheTest(u8, true); + S.doTheTest(*u8, true); + S.doTheTest(f32, true); + S.doTheTest(*f32, true); + // Container with ptr pointing to themselves + S.doTheTest(S0, true); + S.doTheTest(U0, true); +} From 89812217b4e5fee7e2851266c17c9d47204a1573 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 25 Feb 2020 12:22:21 +0100 Subject: [PATCH 19/99] ir: Fix sizeOf comparison with ptr to zst Closes #4536 --- src/analyze.cpp | 5 ++-- test/stage1/behavior/sizeof_and_typeof.zig | 34 ++++++++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 95b2c77129..5ee5f44643 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1132,10 +1132,9 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent if (type_val->special != ConstValSpecialLazy) { assert(type_val->special == ConstValSpecialStatic); if ((type_val->data.x_type->id == ZigTypeIdStruct && - type_val->data.x_type->data.structure.resolve_loop_flag_zero_bits) || + type_val->data.x_type->data.structure.resolve_loop_flag_zero_bits) || (type_val->data.x_type->id == ZigTypeIdUnion && - type_val->data.x_type->data.unionation.resolve_loop_flag_zero_bits) || - type_val->data.x_type->id == ZigTypeIdPointer) + type_val->data.x_type->data.unionation.resolve_loop_flag_zero_bits)) { // Does a struct/union which contains a pointer field to itself have bits? Yes. *is_zero_bits = false; diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig index a738dbbbf9..e3b62b15cc 100644 --- a/test/stage1/behavior/sizeof_and_typeof.zig +++ b/test/stage1/behavior/sizeof_and_typeof.zig @@ -1,5 +1,7 @@ -const builtin = @import("builtin"); -const expect = @import("std").testing.expect; +const std = @import("std"); +const builtin = std.builtin; +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; test "@sizeOf and @TypeOf" { const y: @TypeOf(x) = 120; @@ -135,3 +137,31 @@ test "@bitSizeOf" { a: u2 }) == 2); } + +test "@sizeOf comparison against zero" { + const S0 = struct { + f: *@This(), + }; + const U0 = union { + f: *@This(), + }; + const S = struct { + fn doTheTest(comptime T: type, comptime result: bool) void { + expectEqual(result, @sizeOf(T) > 0); + } + }; + // Zero-sized type + S.doTheTest(u0, false); + S.doTheTest(*u0, false); + // Non byte-sized type + S.doTheTest(u1, true); + S.doTheTest(*u1, true); + // Regular type + S.doTheTest(u8, true); + S.doTheTest(*u8, true); + S.doTheTest(f32, true); + S.doTheTest(*f32, true); + // Container with ptr pointing to themselves + S.doTheTest(S0, true); + S.doTheTest(U0, true); +} From 55ea855e2cefe8dcdb5fab9be66aa6ca3acd0370 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 25 Feb 2020 13:10:29 +0100 Subject: [PATCH 20/99] ir: Various fixes for comptime ptr handling * Correctly fold ptrToInt on optional types * Generate null as ConstPtrSpecialNull in intToPtr * Correctly stop ptrToInt on ?*T where T is zero-sized Closes #4535 --- src/ir.cpp | 28 +++++++++++++++++++++------- test/compile_errors.zig | 9 +++++++++ test/stage1/behavior/pointers.zig | 12 ++++++++++++ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index b0fc8dccd7..77c7c74197 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -27851,9 +27851,15 @@ static IrInstGen *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInst* source_instr, Ir } IrInstGen *result = ir_const(ira, source_instr, ptr_type); - result->value->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - result->value->data.x_ptr.mut = ConstPtrMutRuntimeVar; - result->value->data.x_ptr.data.hard_coded_addr.addr = addr; + if (ptr_type->id == ZigTypeIdOptional && addr == 0) { + result->value->data.x_ptr.special = ConstPtrSpecialNull; + result->value->data.x_ptr.mut = ConstPtrMutComptimeConst; + } else { + result->value->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; + result->value->data.x_ptr.mut = ConstPtrMutRuntimeVar; + result->value->data.x_ptr.data.hard_coded_addr.addr = addr; + } + return result; } @@ -27911,15 +27917,15 @@ static IrInstGen *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstSrcPtr ZigType *usize = ira->codegen->builtin_types.entry_usize; - // We check size explicitly so we can use get_src_ptr_type here. - if (get_src_ptr_type(target->value->type) == nullptr) { + ZigType *src_ptr_type = get_src_ptr_type(target->value->type); + if (src_ptr_type == nullptr) { ir_add_error(ira, &target->base, buf_sprintf("expected pointer, found '%s'", buf_ptr(&target->value->type->name))); return ira->codegen->invalid_inst_gen; } bool has_bits; - if ((err = type_has_bits2(ira->codegen, target->value->type, &has_bits))) + if ((err = type_has_bits2(ira->codegen, src_ptr_type, &has_bits))) return ira->codegen->invalid_inst_gen; if (!has_bits) { @@ -27932,11 +27938,19 @@ static IrInstGen *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstSrcPtr ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_inst_gen; - if (val->type->id == ZigTypeIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { + + // Since we've already run this type trough get_codegen_ptr_type it is + // safe to access the x_ptr fields + if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { IrInstGen *result = ir_const(ira, &instruction->base.base, usize); bigint_init_unsigned(&result->value->data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr); result->value->type = usize; return result; + } else if (val->data.x_ptr.special == ConstPtrSpecialNull) { + IrInstGen *result = ir_const(ira, &instruction->base.base, usize); + bigint_init_unsigned(&result->value->data.x_bigint, 0); + result->value->type = usize; + return result; } } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 56df006e82..138f7a7633 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3,6 +3,15 @@ const builtin = @import("builtin"); const Target = @import("std").Target; pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest("@ptrToInt with pointer to zero-sized type", + \\export fn entry() void { + \\ var pointer: ?*u0 = null; + \\ var x = @ptrToInt(pointer); + \\} + , &[_][]const u8{ + "tmp.zig:3:23: error: pointer to size 0 type has no address", + }); + cases.addTest("slice to pointer conversion mismatch", \\pub fn bytesAsSlice(bytes: var) [*]align(1) const u16 { \\ return @ptrCast([*]align(1) const u16, bytes.ptr)[0..1]; diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig index fdaa5867d7..bcc1d62df3 100644 --- a/test/stage1/behavior/pointers.zig +++ b/test/stage1/behavior/pointers.zig @@ -318,3 +318,15 @@ test "pointer arithmetic affects the alignment" { expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4); } } + +test "@ptrToInt on null optional at comptime" { + { + const pointer = @intToPtr(?*u8, 0x000); + const x = @ptrToInt(pointer); + comptime expect(0 == @ptrToInt(pointer)); + } + { + const pointer = @intToPtr(?*u8, 0xf00); + comptime expect(0xf00 == @ptrToInt(pointer)); + } +} From e9bac8be6b45434f107d2a0d8d4f8fd16de185c6 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 25 Feb 2020 18:35:35 +0100 Subject: [PATCH 21/99] ir: Fix array to slice conversion for zero-sized arrays Closes #3848 --- src/ir.cpp | 17 +++++++++++++---- test/stage1/behavior/slice.zig | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 77c7c74197..8a7976995c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12588,13 +12588,22 @@ static IrInstGen *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInst* sourc { Error err; - if ((err = type_resolve(ira->codegen, array_ptr->value->type->data.pointer.child_type, - ResolveStatusAlignmentKnown))) - { + assert(array_ptr->value->type->id == ZigTypeIdPointer); + + if ((err = type_resolve(ira->codegen, array_ptr->value->type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_inst_gen; } - wanted_type = adjust_slice_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, array_ptr->value->type)); + assert(array_ptr->value->type->data.pointer.child_type->id == ZigTypeIdArray); + + const size_t array_len = array_ptr->value->type->data.pointer.child_type->data.array.len; + + // A zero-sized array can always be casted irregardless of the destination + // alignment + if (array_len != 0) { + wanted_type = adjust_slice_align(ira->codegen, wanted_type, + get_ptr_align(ira->codegen, array_ptr->value->type)); + } if (instr_is_comptime(array_ptr)) { ZigValue *array_ptr_val = ir_resolve_const(ira, array_ptr, UndefBad); diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig index b985ec1f8e..c0fa723cfb 100644 --- a/test/stage1/behavior/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -1,6 +1,7 @@ const std = @import("std"); const expect = std.testing.expect; const expectEqualSlices = std.testing.expectEqualSlices; +const expectEqual = std.testing.expectEqual; const mem = std.mem; const x = @intToPtr([*]i32, 0x1000)[0..0x500]; @@ -97,3 +98,20 @@ test "obtaining a null terminated slice" { comptime expect(@TypeOf(ptr2) == [:0]u8); comptime expect(@TypeOf(ptr2[0..2]) == []u8); } + +test "empty array to slice" { + const S = struct { + fn doTheTest() void { + const empty: []align(16) u8 = &[_]u8{}; + const align_1: []align(1) u8 = empty; + const align_4: []align(4) u8 = empty; + const align_16: []align(16) u8 = empty; + expectEqual(1, @typeInfo(@TypeOf(align_1)).Pointer.alignment); + expectEqual(4, @typeInfo(@TypeOf(align_4)).Pointer.alignment); + expectEqual(16, @typeInfo(@TypeOf(align_16)).Pointer.alignment); + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} From dad62a7e27fb7e8ea1eb51d6619fd534106417f1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 25 Feb 2020 21:23:35 -0500 Subject: [PATCH 22/99] Revert "ir: Fix sizeOf comparison with ptr to zst" This reverts commit 89812217b4e5fee7e2851266c17c9d47204a1573. This caused #4560 --- src/analyze.cpp | 5 ++-- test/stage1/behavior/sizeof_and_typeof.zig | 34 ++-------------------- 2 files changed, 5 insertions(+), 34 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 5ee5f44643..95b2c77129 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1132,9 +1132,10 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent if (type_val->special != ConstValSpecialLazy) { assert(type_val->special == ConstValSpecialStatic); if ((type_val->data.x_type->id == ZigTypeIdStruct && - type_val->data.x_type->data.structure.resolve_loop_flag_zero_bits) || + type_val->data.x_type->data.structure.resolve_loop_flag_zero_bits) || (type_val->data.x_type->id == ZigTypeIdUnion && - type_val->data.x_type->data.unionation.resolve_loop_flag_zero_bits)) + type_val->data.x_type->data.unionation.resolve_loop_flag_zero_bits) || + type_val->data.x_type->id == ZigTypeIdPointer) { // Does a struct/union which contains a pointer field to itself have bits? Yes. *is_zero_bits = false; diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig index e3b62b15cc..a738dbbbf9 100644 --- a/test/stage1/behavior/sizeof_and_typeof.zig +++ b/test/stage1/behavior/sizeof_and_typeof.zig @@ -1,7 +1,5 @@ -const std = @import("std"); -const builtin = std.builtin; -const expect = std.testing.expect; -const expectEqual = std.testing.expectEqual; +const builtin = @import("builtin"); +const expect = @import("std").testing.expect; test "@sizeOf and @TypeOf" { const y: @TypeOf(x) = 120; @@ -137,31 +135,3 @@ test "@bitSizeOf" { a: u2 }) == 2); } - -test "@sizeOf comparison against zero" { - const S0 = struct { - f: *@This(), - }; - const U0 = union { - f: *@This(), - }; - const S = struct { - fn doTheTest(comptime T: type, comptime result: bool) void { - expectEqual(result, @sizeOf(T) > 0); - } - }; - // Zero-sized type - S.doTheTest(u0, false); - S.doTheTest(*u0, false); - // Non byte-sized type - S.doTheTest(u1, true); - S.doTheTest(*u1, true); - // Regular type - S.doTheTest(u8, true); - S.doTheTest(*u8, true); - S.doTheTest(f32, true); - S.doTheTest(*f32, true); - // Container with ptr pointing to themselves - S.doTheTest(S0, true); - S.doTheTest(U0, true); -} From e75598af3d011c6277d9d715f39d112db948d5e0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 25 Feb 2020 21:24:27 -0500 Subject: [PATCH 23/99] add test case to catch regression from previous commit Once this test case is passing, previous commit can be re-added. Closes #4560 --- test/stage1/behavior.zig | 1 + test/stage1/behavior/bugs/4560.zig | 32 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 test/stage1/behavior/bugs/4560.zig diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 66893519f4..ff3e2ac284 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -40,6 +40,7 @@ comptime { _ = @import("behavior/bugs/3384.zig"); _ = @import("behavior/bugs/3586.zig"); _ = @import("behavior/bugs/3742.zig"); + _ = @import("behavior/bugs/4560.zig"); _ = @import("behavior/bugs/394.zig"); _ = @import("behavior/bugs/421.zig"); _ = @import("behavior/bugs/529.zig"); diff --git a/test/stage1/behavior/bugs/4560.zig b/test/stage1/behavior/bugs/4560.zig new file mode 100644 index 0000000000..fb47376f8e --- /dev/null +++ b/test/stage1/behavior/bugs/4560.zig @@ -0,0 +1,32 @@ +const std = @import("std"); + +test "fixed" { + var s: S = .{ + .a = 1, + .b = .{ + .size = 123, + .max_distance_from_start_index = 456, + }, + }; + std.testing.expect(s.a == 1); + std.testing.expect(s.b.size == 123); + std.testing.expect(s.b.max_distance_from_start_index == 456); +} + +const S = struct { + a: u32, + b: Map, + + const Map = std.StringHashMap(*S); +}; + +pub fn StringHashMap(comptime V: type) type { + return HashMap([]const u8, V); +} + +pub fn HashMap(comptime K: type, comptime V: type) type { + return struct { + size: usize, + max_distance_from_start_index: usize, + }; +} From a55e5363917befcb93575c256a3ce8fc150e0666 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 25 Feb 2020 21:32:03 -0500 Subject: [PATCH 24/99] fix typo in previous commit oops, forgot to actually run the test --- test/stage1/behavior/bugs/4560.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/stage1/behavior/bugs/4560.zig b/test/stage1/behavior/bugs/4560.zig index fb47376f8e..6821527894 100644 --- a/test/stage1/behavior/bugs/4560.zig +++ b/test/stage1/behavior/bugs/4560.zig @@ -17,7 +17,7 @@ const S = struct { a: u32, b: Map, - const Map = std.StringHashMap(*S); + const Map = StringHashMap(*S); }; pub fn StringHashMap(comptime V: type) type { From d2535c003c6188fcc362028e01ef9f7fb3356727 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 26 Feb 2020 10:05:04 +0100 Subject: [PATCH 25/99] ir: Fix regression with self-referencing containers --- src/analyze.cpp | 19 ++++++++++++++----- test/stage1/behavior/sizeof_and_typeof.zig | 22 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 5ee5f44643..41ae69f803 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1131,17 +1131,26 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent Error err; if (type_val->special != ConstValSpecialLazy) { assert(type_val->special == ConstValSpecialStatic); - if ((type_val->data.x_type->id == ZigTypeIdStruct && - type_val->data.x_type->data.structure.resolve_loop_flag_zero_bits) || - (type_val->data.x_type->id == ZigTypeIdUnion && - type_val->data.x_type->data.unionation.resolve_loop_flag_zero_bits)) + + // Self-referencing types via pointers are allowed and have non-zero size + ZigType *ty = type_val->data.x_type; + while (ty->id == ZigTypeIdPointer && + !ty->data.unionation.resolve_loop_flag_zero_bits) + { + ty = ty->data.pointer.child_type; + } + + if ((ty->id == ZigTypeIdStruct && ty->data.structure.resolve_loop_flag_zero_bits) || + (ty->id == ZigTypeIdUnion && ty->data.unionation.resolve_loop_flag_zero_bits) || + (ty->id == ZigTypeIdPointer && ty->data.pointer.resolve_loop_flag_zero_bits)) { - // Does a struct/union which contains a pointer field to itself have bits? Yes. *is_zero_bits = false; return ErrorNone; } + if ((err = type_resolve(g, type_val->data.x_type, ResolveStatusZeroBitsKnown))) return err; + *is_zero_bits = (type_val->data.x_type->abi_size == 0); return ErrorNone; } diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig index e3b62b15cc..322c5fbbad 100644 --- a/test/stage1/behavior/sizeof_and_typeof.zig +++ b/test/stage1/behavior/sizeof_and_typeof.zig @@ -145,6 +145,26 @@ test "@sizeOf comparison against zero" { const U0 = union { f: *@This(), }; + const S1 = struct { + fn H(comptime T: type) type { + return struct { + x: T, + }; + } + f0: H(*@This()), + f1: H(**@This()), + f2: H(***@This()), + }; + const U1 = union { + fn H(comptime T: type) type { + return struct { + x: T, + }; + } + f0: H(*@This()), + f1: H(**@This()), + f2: H(***@This()), + }; const S = struct { fn doTheTest(comptime T: type, comptime result: bool) void { expectEqual(result, @sizeOf(T) > 0); @@ -164,4 +184,6 @@ test "@sizeOf comparison against zero" { // Container with ptr pointing to themselves S.doTheTest(S0, true); S.doTheTest(U0, true); + S.doTheTest(S1, true); + S.doTheTest(U1, true); } From d505ea6cafe8bf6549eb2c56396b0a94d8109859 Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 26 Feb 2020 01:03:58 +0200 Subject: [PATCH 26/99] fix `@tagName` on extern and non-exhaustive enums --- src/codegen.cpp | 17 +++++++++++------ src/ir.cpp | 11 +++++++---- test/compile_errors.zig | 9 +++++++++ test/stage1/behavior/enum.zig | 23 ++++++++++++++++++++++- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index f9d2566da7..0bf8510843 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5031,8 +5031,18 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) { LLVMConstNull(usize->llvm_type), }; + HashMap occupied_tag_values = {}; + occupied_tag_values.init(field_count); + for (size_t field_i = 0; field_i < field_count; field_i += 1) { - Buf *name = enum_type->data.enumeration.fields[field_i].name; + TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; + + Buf *name = type_enum_field->name; + auto entry = occupied_tag_values.put_unique(type_enum_field->value, name); + if (entry != nullptr) { + continue; + } + LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true); LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), ""); LLVMSetInitializer(str_global, str_init); @@ -5086,11 +5096,6 @@ static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutableGen *executa { ZigType *enum_type = instruction->target->value->type; assert(enum_type->id == ZigTypeIdEnum); - if (enum_type->data.enumeration.non_exhaustive) { - add_node_error(g, instruction->base.base.source_node, - buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991")); - codegen_report_errors_and_exit(g); - } LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type); diff --git a/src/ir.cpp b/src/ir.cpp index 8a7976995c..4d24ec7dfb 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -23190,12 +23190,15 @@ static IrInstGen *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstSrc if (instr_is_comptime(target)) { if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; - if (target->value->type->data.enumeration.non_exhaustive) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991")); + TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint); + if (field == nullptr) { + Buf *int_buf = buf_alloc(); + bigint_append_buf(int_buf, &target->value->data.x_bigint, 10); + + ir_add_error(ira, &target->base, + buf_sprintf("no tag by value %s", buf_ptr(int_buf))); return ira->codegen->invalid_inst_gen; } - TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint); ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee; IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(field->name), true); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 138f7a7633..0260160dca 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3,6 +3,15 @@ const builtin = @import("builtin"); const Target = @import("std").Target; pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest("@tagName on invalid value of non-exhaustive enum", + \\test "enum" { + \\ const E = enum(u8) {A, B, _}; + \\ _ = @tagName(@intToEnum(E, 5)); + \\} + , &[_][]const u8{ + "tmp.zig:3:18: error: no tag by value 5", + }); + cases.addTest("@ptrToInt with pointer to zero-sized type", \\export fn entry() void { \\ var pointer: ?*u0 = null; diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig index 18489ba24b..b6cb86a363 100644 --- a/test/stage1/behavior/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -198,7 +198,17 @@ test "@tagName" { comptime expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); } -fn testEnumTagNameBare(n: BareNumber) []const u8 { +test "@tagName extern enum with duplicates" { + expect(mem.eql(u8, testEnumTagNameBare(ExternDuplicates.B), "A")); + comptime expect(mem.eql(u8, testEnumTagNameBare(ExternDuplicates.B), "A")); +} + +test "@tagName non-exhaustive enum" { + expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); + comptime expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); +} + +fn testEnumTagNameBare(n: var) []const u8 { return @tagName(n); } @@ -208,6 +218,17 @@ const BareNumber = enum { Three, }; +const ExternDuplicates = extern enum(u8) { + A = 1, + B = 1, +}; + +const NonExhaustive = enum(u8) { + A, + B, + _, +}; + test "enum alignment" { comptime { expect(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); From 215797749d0e0d911feaaf45dc70836c3d12f18e Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 26 Feb 2020 01:19:22 +0200 Subject: [PATCH 27/99] fix `@intToEnum` on extern enums --- src/codegen.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 0bf8510843..4d992d25d0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3329,9 +3329,21 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutableGen *executabl LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue"); size_t field_count = wanted_type->data.enumeration.src_field_count; LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count); + + HashMap occupied_tag_values = {}; + occupied_tag_values.init(field_count); + for (size_t field_i = 0; field_i < field_count; field_i += 1) { + TypeEnumField *type_enum_field = &wanted_type->data.enumeration.fields[field_i]; + + Buf *name = type_enum_field->name; + auto entry = occupied_tag_values.put_unique(type_enum_field->value, name); + if (entry != nullptr) { + continue; + } + LLVMValueRef this_tag_int_value = bigint_to_llvm_const(get_llvm_type(g, tag_int_type), - &wanted_type->data.enumeration.fields[field_i].value); + &type_enum_field->value); LLVMAddCase(switch_instr, this_tag_int_value, ok_value_block); } LLVMPositionBuilderAtEnd(g->builder, bad_value_block); From 22432b15e31d506d070171b2932d5df71e9a1b9e Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 26 Feb 2020 11:07:47 +0200 Subject: [PATCH 28/99] add test for `@intToEnum` --- lib/std/fmt.zig | 6 ++---- src-self-hosted/ir.zig | 2 +- test/stage1/behavior/cast.zig | 11 +++++++++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index b9c6dd0033..d093101646 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -414,10 +414,9 @@ pub fn formatType( if (max_depth == 0) { return output(context, "{ ... }"); } - comptime var field_i = 0; try output(context, "{"); - inline for (StructT.fields) |f| { - if (field_i == 0) { + inline for (StructT.fields) |f, i| { + if (i == 0) { try output(context, " ."); } else { try output(context, ", ."); @@ -425,7 +424,6 @@ pub fn formatType( try output(context, f.name); try output(context, " = "); try formatType(@field(value, f.name), fmt, options, context, Errors, output, max_depth - 1); - field_i += 1; } try output(context, " }"); }, diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 576294a7d7..2e65962d41 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -1803,7 +1803,7 @@ pub const Builder = struct { // Look at the params and ref() other instructions inline for (@typeInfo(I.Params).Struct.fields) |f| { - switch (f.fiedl_type) { + switch (f.field_type) { *Inst => @field(inst.params, f.name).ref(self), *BasicBlock => @field(inst.params, f.name).ref(self), ?*Inst => if (@field(inst.params, f.name)) |other| other.ref(self), diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index 76d3b784ec..d2267a3753 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -491,6 +491,17 @@ test "@intToEnum passed a comptime_int to an enum with one item" { expect(x == E.A); } +test "@intToEnum runtime to an extern enum with duplicate values" { + const E = extern enum(u8) { + A = 1, + B = 1, + }; + var a: u8 = 1; + var x = @intToEnum(E, a); + expect(x == E.A); + expect(x == E.B); +} + test "@intCast to u0 and use the result" { const S = struct { fn doTheTest(zero: u1, one: u1, bigzero: i32) void { From 62de32a18c1eecedc29055e4199fa364f9e9b7c6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 11:21:10 -0500 Subject: [PATCH 29/99] call deinit on the hashmaps introduced in the prev commit --- src/codegen.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index 4d992d25d0..ee3e05a801 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3346,6 +3346,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutableGen *executabl &type_enum_field->value); LLVMAddCase(switch_instr, this_tag_int_value, ok_value_block); } + occupied_tag_values.deinit(); LLVMPositionBuilderAtEnd(g->builder, bad_value_block); gen_safety_crash(g, PanicMsgIdBadEnumValue); @@ -5084,6 +5085,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) { LLVMPositionBuilderAtEnd(g->builder, return_block); LLVMBuildRet(g->builder, slice_global); } + occupied_tag_values.deinit(); LLVMPositionBuilderAtEnd(g->builder, bad_value_block); if (g->build_mode == BuildModeDebug || g->build_mode == BuildModeSafeRelease) { From 2b33e27e1c72703deb50bef5c486b86130a75687 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 12:37:13 -0500 Subject: [PATCH 30/99] arm baseline CPU is v7a rather than v6m v6m is thumb-mode and the baseline should probably be an 'a' variant. --- lib/std/target/arm.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/target/arm.zig b/lib/std/target/arm.zig index 50c1bfed15..acb8318bde 100644 --- a/lib/std/target/arm.zig +++ b/lib/std/target/arm.zig @@ -1509,7 +1509,7 @@ pub const cpu = struct { .name = "baseline", .llvm_name = "generic", .features = featureSet(&[_]Feature{ - .v6m, + .v7a, }), }; pub const cortex_a12 = CpuModel{ From 0a8835268915feab561bdb68adb17aed76b9708f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 13:17:38 -0500 Subject: [PATCH 31/99] fix behavior tests with --test-evented-io --- lib/std/debug.zig | 26 +++++++++++++------------- lib/std/fs.zig | 11 +++++++++-- lib/std/fs/file.zig | 7 ++++++- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index a9f9819a25..343df9bde0 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -811,22 +811,22 @@ pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !M const mapped_mem = try mapWholeFile(elf_file_path); var seekable_stream = io.SliceSeekableInStream.init(mapped_mem); - var efile = try elf.Elf.openStream( + var efile = try noasync elf.Elf.openStream( allocator, @ptrCast(*DW.DwarfSeekableStream, &seekable_stream.seekable_stream), @ptrCast(*DW.DwarfInStream, &seekable_stream.stream), ); - defer efile.close(); + defer noasync efile.close(); - const debug_info = (try efile.findSection(".debug_info")) orelse + const debug_info = (try noasync efile.findSection(".debug_info")) orelse return error.MissingDebugInfo; - const debug_abbrev = (try efile.findSection(".debug_abbrev")) orelse + const debug_abbrev = (try noasync efile.findSection(".debug_abbrev")) orelse return error.MissingDebugInfo; - const debug_str = (try efile.findSection(".debug_str")) orelse + const debug_str = (try noasync efile.findSection(".debug_str")) orelse return error.MissingDebugInfo; - const debug_line = (try efile.findSection(".debug_line")) orelse + const debug_line = (try noasync efile.findSection(".debug_line")) orelse return error.MissingDebugInfo; - const opt_debug_ranges = try efile.findSection(".debug_ranges"); + const opt_debug_ranges = try noasync efile.findSection(".debug_ranges"); var di = DW.DwarfInfo{ .endian = efile.endian, @@ -840,7 +840,7 @@ pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !M null, }; - try DW.openDwarfDebugInfo(&di, allocator); + try noasync DW.openDwarfDebugInfo(&di, allocator); return ModuleDebugInfo{ .base_address = undefined, @@ -983,8 +983,8 @@ const MachoSymbol = struct { }; fn mapWholeFile(path: []const u8) ![]const u8 { - const file = try fs.openFileAbsolute(path, .{}); - defer file.close(); + const file = try noasync fs.openFileAbsolute(path, .{ .always_blocking = true }); + defer noasync file.close(); const file_len = try math.cast(usize, try file.getEndPos()); const mapped_mem = try os.mmap( @@ -1565,14 +1565,14 @@ pub const ModuleDebugInfo = switch (builtin.os) { // Translate the VA into an address into this object const relocated_address = address - self.base_address; - if (self.dwarf.findCompileUnit(relocated_address)) |compile_unit| { + if (noasync self.dwarf.findCompileUnit(relocated_address)) |compile_unit| { return SymbolInfo{ - .symbol_name = self.dwarf.getSymbolName(relocated_address) orelse "???", + .symbol_name = noasync self.dwarf.getSymbolName(relocated_address) orelse "???", .compile_unit_name = compile_unit.die.getAttrString(&self.dwarf, DW.AT_name) catch |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => "???", else => return err, }, - .line_info = self.dwarf.getLineNumberInfo(compile_unit.*, relocated_address) catch |err| switch (err) { + .line_info = noasync self.dwarf.getLineNumberInfo(compile_unit.*, relocated_address) catch |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => null, else => return err, }, diff --git a/lib/std/fs.zig b/lib/std/fs.zig index c3d418eb8c..ecd2c2b750 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -731,11 +731,18 @@ pub const Dir = struct { @as(u32, os.O_WRONLY) else @as(u32, os.O_RDONLY); - const fd = if (need_async_thread) + const fd = if (need_async_thread and !flags.always_blocking) try std.event.Loop.instance.?.openatZ(self.fd, sub_path, os_flags, 0) else try os.openatC(self.fd, sub_path, os_flags, 0); - return File{ .handle = fd, .io_mode = .blocking }; + return File{ + .handle = fd, + .io_mode = .blocking, + .async_block_allowed = if (flags.always_blocking) + File.async_block_allowed_yes + else + File.async_block_allowed_no, + }; } /// Same as `openFile` but Windows-only and the path parameter is diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index a3a428e77b..9d0ffcfd4a 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -20,7 +20,7 @@ pub const File = struct { /// or, more specifically, whether the I/O is blocking. io_mode: io.Mode, - /// Even when std.io.mode is async, it is still sometimes desirable to perform blocking I/O, although + /// Even when 'std.io.mode' is async, it is still sometimes desirable to perform blocking I/O, although /// not by default. For example, when printing a stack trace to stderr. async_block_allowed: @TypeOf(async_block_allowed_no) = async_block_allowed_no, @@ -40,6 +40,11 @@ pub const File = struct { pub const OpenFlags = struct { read: bool = true, write: bool = false, + + /// This prevents `O_NONBLOCK` from being passed even if `std.io.is_async`. + /// It allows the use of `noasync` when calling functions related to opening + /// the file, reading, and writing. + always_blocking: bool = false, }; /// TODO https://github.com/ziglang/zig/issues/3802 From fd1eade4ca02b125b1a2ecea564f32af6a683248 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 26 Feb 2020 23:17:44 +0100 Subject: [PATCH 32/99] ir: Allow empty inferred error sets Closes #4564 --- src/analyze.cpp | 51 ++++++++++++++++++++----------------- test/compile_errors.zig | 10 -------- test/stage1/behavior/fn.zig | 14 +++++++++- 3 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 41ae69f803..9b776da930 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4771,38 +4771,41 @@ static void analyze_fn_ir(CodeGen *g, ZigFn *fn, AstNode *return_type_node) { if (return_err_set_type->data.error_set.infer_fn != nullptr && return_err_set_type->data.error_set.incomplete) { - ZigType *inferred_err_set_type; + // The inferred error set type is null if the function doesn't + // return any error + ZigType *inferred_err_set_type = nullptr; + if (fn->src_implicit_return_type->id == ZigTypeIdErrorSet) { inferred_err_set_type = fn->src_implicit_return_type; } else if (fn->src_implicit_return_type->id == ZigTypeIdErrorUnion) { inferred_err_set_type = fn->src_implicit_return_type->data.error_union.err_set_type; - } else { - add_node_error(g, return_type_node, - buf_sprintf("function with inferred error set must return at least one possible error")); - fn->anal_state = FnAnalStateInvalid; - return; } - if (inferred_err_set_type->data.error_set.infer_fn != nullptr && - inferred_err_set_type->data.error_set.incomplete) - { - if (!resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) { - fn->anal_state = FnAnalStateInvalid; - return; - } - } - - return_err_set_type->data.error_set.incomplete = false; - if (type_is_global_error_set(inferred_err_set_type)) { - return_err_set_type->data.error_set.err_count = UINT32_MAX; - } else { - return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count; - if (inferred_err_set_type->data.error_set.err_count > 0) { - return_err_set_type->data.error_set.errors = heap::c_allocator.allocate(inferred_err_set_type->data.error_set.err_count); - for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) { - return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i]; + if (inferred_err_set_type != nullptr) { + if (inferred_err_set_type->data.error_set.infer_fn != nullptr && + inferred_err_set_type->data.error_set.incomplete) + { + if (!resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) { + fn->anal_state = FnAnalStateInvalid; + return; } } + + return_err_set_type->data.error_set.incomplete = false; + if (type_is_global_error_set(inferred_err_set_type)) { + return_err_set_type->data.error_set.err_count = UINT32_MAX; + } else { + return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count; + if (inferred_err_set_type->data.error_set.err_count > 0) { + return_err_set_type->data.error_set.errors = heap::c_allocator.allocate(inferred_err_set_type->data.error_set.err_count); + for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) { + return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i]; + } + } + } + } else { + return_err_set_type->data.error_set.incomplete = false; + return_err_set_type->data.error_set.err_count = 0; } } } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 0260160dca..5e4f76baf4 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2734,16 +2734,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:5:5: error: else prong required when switching on type 'anyerror'", }); - cases.add("inferred error set with no returned error", - \\export fn entry() void { - \\ foo() catch unreachable; - \\} - \\fn foo() !void { - \\} - , &[_][]const u8{ - "tmp.zig:4:11: error: function with inferred error set must return at least one possible error", - }); - cases.add("error not handled in switch", \\export fn entry() void { \\ foo(452) catch |err| switch (err) { diff --git a/test/stage1/behavior/fn.zig b/test/stage1/behavior/fn.zig index 13859b92ca..c1e5459378 100644 --- a/test/stage1/behavior/fn.zig +++ b/test/stage1/behavior/fn.zig @@ -1,4 +1,7 @@ -const expect = @import("std").testing.expect; +const std = @import("std"); +const testing = std.testing; +const expect = testing.expect; +const expectEqual = testing.expectEqual; test "params" { expect(testParamsAdd(22, 11) == 33); @@ -272,3 +275,12 @@ test "ability to give comptime types and non comptime types to same parameter" { S.doTheTest(); comptime S.doTheTest(); } + +test "function with inferred error set but returning no error" { + const S = struct { + fn foo() !void {} + }; + + const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?; + expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len); +} From 6a0927d8c18d62d131e0ecda945398c3bb0ffa1a Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 26 Feb 2020 21:41:29 +0100 Subject: [PATCH 33/99] debug: Fix end-of-stream condition in DWARF parser --- lib/std/dwarf.zig | 10 ++++++---- lib/std/io/seekable_stream.zig | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index f174e38555..32a49b68e0 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -417,9 +417,9 @@ pub const DwarfInfo = struct { var s = io.SliceSeekableInStream.init(di.debug_info); var this_unit_offset: u64 = 0; - while (true) { + while (this_unit_offset < try s.seekable_stream.getEndPos()) { s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) { - error.EndOfStream => return, + error.EndOfStream => unreachable, else => return err, }; @@ -445,6 +445,8 @@ pub const DwarfInfo = struct { while ((try s.seekable_stream.getPos()) < next_unit_pos) { const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue; + defer die_obj.attrs.deinit(); + const after_die_offset = try s.seekable_stream.getPos(); switch (die_obj.tag_id) { @@ -520,9 +522,9 @@ pub const DwarfInfo = struct { var s = io.SliceSeekableInStream.init(di.debug_info); var this_unit_offset: u64 = 0; - while (true) { + while (this_unit_offset < try s.seekable_stream.getEndPos()) { s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) { - error.EndOfStream => return, + error.EndOfStream => unreachable, else => return err, }; diff --git a/lib/std/io/seekable_stream.zig b/lib/std/io/seekable_stream.zig index 48dc31b785..052abbc856 100644 --- a/lib/std/io/seekable_stream.zig +++ b/lib/std/io/seekable_stream.zig @@ -73,7 +73,7 @@ pub const SliceSeekableInStream = struct { fn seekToFn(in_stream: *SeekableInStream, pos: u64) SeekError!void { const self = @fieldParentPtr(Self, "seekable_stream", in_stream); const usize_pos = @intCast(usize, pos); - if (usize_pos >= self.slice.len) return error.EndOfStream; + if (usize_pos > self.slice.len) return error.EndOfStream; self.pos = usize_pos; } @@ -86,7 +86,7 @@ pub const SliceSeekableInStream = struct { self.pos -= abs_amt; } else { const usize_amt = @intCast(usize, amt); - if (self.pos + usize_amt >= self.slice.len) return error.EndOfStream; + if (self.pos + usize_amt > self.slice.len) return error.EndOfStream; self.pos += usize_amt; } } From 2696c8b42d7fcec1b8cf3f332742df4638c519b1 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 27 Feb 2020 13:02:21 +0100 Subject: [PATCH 34/99] ir: Robust checking for init expr type Closes #3979 --- src/ir.cpp | 14 +++++++------- test/compile_errors.zig | 9 +++++++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 4d24ec7dfb..7de45ebcd3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14875,19 +14875,19 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr, // cast from inferred struct type to array, union, or struct if (is_anon_container(actual_type)) { - AstNode *decl_node = actual_type->data.structure.decl_node; - ir_assert(decl_node->type == NodeTypeContainerInitExpr, source_instr); - ContainerInitKind init_kind = decl_node->data.container_init_expr.kind; - uint32_t field_count = actual_type->data.structure.src_field_count; - if (wanted_type->id == ZigTypeIdArray && (init_kind == ContainerInitKindArray || field_count == 0) && + const bool is_array_init = + actual_type->data.structure.special == StructSpecialInferredTuple; + const uint32_t field_count = actual_type->data.structure.src_field_count; + + if (wanted_type->id == ZigTypeIdArray && (is_array_init || field_count == 0) && wanted_type->data.array.len == field_count) { return ir_analyze_struct_literal_to_array(ira, source_instr, value, wanted_type); } else if (wanted_type->id == ZigTypeIdStruct && - (init_kind == ContainerInitKindStruct || field_count == 0)) + (!is_array_init || field_count == 0)) { return ir_analyze_struct_literal_to_struct(ira, source_instr, value, wanted_type); - } else if (wanted_type->id == ZigTypeIdUnion && init_kind == ContainerInitKindStruct && field_count == 1) { + } else if (wanted_type->id == ZigTypeIdUnion && !is_array_init && field_count == 1) { return ir_analyze_struct_literal_to_union(ira, source_instr, value, wanted_type); } } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 5e4f76baf4..91c17d8807 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3,6 +3,15 @@ const builtin = @import("builtin"); const Target = @import("std").Target; pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest("type mismatch with tuple concatenation", + \\export fn entry() void { + \\ var x = .{}; + \\ x = x ++ .{ 1, 2, 3 }; + \\} + , &[_][]const u8{ + "tmp.zig:3:11: error: expected type 'struct:2:14', found 'struct:3:11'", + }); + cases.addTest("@tagName on invalid value of non-exhaustive enum", \\test "enum" { \\ const E = enum(u8) {A, B, _}; From c39d7a632603c944732787e3ba1b77e26fccc3c1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 11:51:43 -0500 Subject: [PATCH 35/99] fix exported variable not respecting linkage --- src/ir.cpp | 1 + test/standalone/global_linkage/build.zig | 23 +++++++++++++++++++++++ test/standalone/global_linkage/main.zig | 9 +++++++++ test/standalone/global_linkage/obj1.zig | 7 +++++++ test/standalone/global_linkage/obj2.zig | 7 +++++++ 5 files changed, 47 insertions(+) create mode 100644 test/standalone/global_linkage/build.zig create mode 100644 test/standalone/global_linkage/main.zig create mode 100644 test/standalone/global_linkage/obj1.zig create mode 100644 test/standalone/global_linkage/obj2.zig diff --git a/src/ir.cpp b/src/ir.cpp index 7de45ebcd3..895cfa334c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -17841,6 +17841,7 @@ static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport } } break; case ZigTypeIdInt: + want_var_export = true; break; case ZigTypeIdVoid: case ZigTypeIdBool: diff --git a/test/standalone/global_linkage/build.zig b/test/standalone/global_linkage/build.zig new file mode 100644 index 0000000000..c6c45c008a --- /dev/null +++ b/test/standalone/global_linkage/build.zig @@ -0,0 +1,23 @@ +const Builder = @import("std").build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + const target = b.standardTargetOptions(null); + + const obj1 = b.addStaticLibrary("obj1", "obj1.zig"); + obj1.setBuildMode(mode); + obj1.setTheTarget(target); + + const obj2 = b.addStaticLibrary("obj2", "obj2.zig"); + obj2.setBuildMode(mode); + obj2.setTheTarget(target); + + const main = b.addTest("main.zig"); + main.setBuildMode(mode); + main.setTheTarget(target); + main.linkLibrary(obj1); + main.linkLibrary(obj2); + + const test_step = b.step("test", "Test it"); + test_step.dependOn(&main.step); +} diff --git a/test/standalone/global_linkage/main.zig b/test/standalone/global_linkage/main.zig new file mode 100644 index 0000000000..53d953765b --- /dev/null +++ b/test/standalone/global_linkage/main.zig @@ -0,0 +1,9 @@ +const std = @import("std"); + +extern var obj1_integer: usize; +extern var obj2_integer: usize; + +test "access the external integers" { + std.testing.expect(obj1_integer == 421); + std.testing.expect(obj2_integer == 422); +} diff --git a/test/standalone/global_linkage/obj1.zig b/test/standalone/global_linkage/obj1.zig new file mode 100644 index 0000000000..95814cda3d --- /dev/null +++ b/test/standalone/global_linkage/obj1.zig @@ -0,0 +1,7 @@ +extern var internal_integer: usize = 1; +extern var obj1_integer: usize = 421; + +comptime { + @export(internal_integer, .{ .name = "internal_integer", .linkage = .Internal }); + @export(obj1_integer, .{ .name = "obj1_integer", .linkage = .Strong }); +} diff --git a/test/standalone/global_linkage/obj2.zig b/test/standalone/global_linkage/obj2.zig new file mode 100644 index 0000000000..f2d44b2dc0 --- /dev/null +++ b/test/standalone/global_linkage/obj2.zig @@ -0,0 +1,7 @@ +extern var internal_integer: usize = 2; +extern var obj2_integer: usize = 422; + +comptime { + @export(internal_integer, .{ .name = "internal_integer", .linkage = .Internal }); + @export(obj2_integer, .{ .name = "obj2_integer", .linkage = .Strong }); +} From a6087a7bc1e7ba5e964e20379f3ea70bd8c25f1e Mon Sep 17 00:00:00 2001 From: Michael Dusan Date: Fri, 28 Feb 2020 08:30:43 -0500 Subject: [PATCH 36/99] stage1: housekeeping --- src/all_types.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 022a34038e..7277d04359 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3267,7 +3267,6 @@ struct IrInstSrcContainerInitList { struct IrInstSrcContainerInitFieldsField { Buf *name; AstNode *source_node; - TypeStructField *type_struct_field; IrInstSrc *result_loc; }; From fba39ff331a84f1a32d076ccbb8b87cd02ea7121 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 24 Feb 2020 01:38:21 -0500 Subject: [PATCH 37/99] restructuring std.Target for OS version ranges, pass 1 --- lib/std/builtin.zig | 32 +++- lib/std/target.zig | 430 ++++++++++++++++++++++++++++++++++---------- src/analyze.cpp | 2 +- test/tests.zig | 30 ++-- 4 files changed, 383 insertions(+), 111 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 5440de4d3b..1bb8fd85ae 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -398,7 +398,37 @@ pub const LinkMode = enum { pub const Version = struct { major: u32, minor: u32, - patch: u32, + patch: u32 = 0, + + pub const Range = struct { + min: Version, + max: Version, + + pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool { + if (self.min.compare(ver) == .gt) return false; + if (self.max.compare(ver) == .lt) return false; + return true; + } + }; + + pub fn order(lhs: Version, rhs: version) std.math.Order { + if (lhs.major < rhs.major) return .lt; + if (lhs.major > rhs.major) return .gt; + if (lhs.minor < rhs.minor) return .lt; + if (lhs.minor > rhs.minor) return .gt; + if (lhs.patch < rhs.patch) return .lt; + if (lhs.patch > rhs.patch) return .gt; + return .eq; + } + + pub fn parse(text: []const u8) !Version { + var it = std.mem.separate(text, "."); + return Version{ + .major = try std.fmt.parseInt(u32, it.next() orelse return error.InvalidVersion, 10), + .minor = try std.fmt.parseInt(u32, it.next() orelse "0", 10), + .patch = try std.fmt.parseInt(u32, it.next() orelse "0", 10), + }; + } }; /// This data structure is used by the Zig language code generation and diff --git a/lib/std/target.zig b/lib/std/target.zig index cf83bb1f7a..84056eae02 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1,30 +1,53 @@ const std = @import("std.zig"); const mem = std.mem; const builtin = std.builtin; +const Version = std.builtin.Version; /// TODO Nearly all the functions in this namespace would be /// better off if https://github.com/ziglang/zig/issues/425 /// was solved. -pub const Target = union(enum) { - Native: void, - Cross: Cross, +pub const Target = struct { + cpu: Cpu, + os: Os, + abi: Abi, - pub const Os = enum { + /// The version ranges here represent the minimum OS version to be supported + /// and the maximum OS version to be supported. The default values represent + /// the range that the Zig Standard Library bases its abstractions on. + /// + /// The minimum version of the range is the main setting to tweak for a target. + /// Usually, the maximum target OS version will remain the default, which is + /// the latest released version of the OS. + /// + /// To test at compile time if the target is guaranteed to support a given OS feature, + /// one should check that the minimum version of the range is greater than or equal to + /// the version the feature was introduced in. + /// + /// To test at compile time if the target certainly will not support a given OS feature, + /// one should check that the maximum version of the range is less than the version the + /// feature was introduced in. + /// + /// If neither of these cases apply, a runtime check should be used to determine if the + /// target supports a given OS feature. + /// + /// Binaries built with a given maximum version will continue to function on newer operating system + /// versions. However, such a binary may not take full advantage of the newer operating system APIs. + pub const Os = union(enum) { freestanding, ananas, cloudabi, dragonfly, - freebsd, + freebsd: Version.Range, fuchsia, ios, kfreebsd, - linux, + linux: LinuxVersionRange, lv2, - macosx, - netbsd, - openbsd, + macosx: Version.Range, + netbsd: Version.Range, + openbsd: Version.Range, solaris, - windows, + windows: WindowsVersion.Range, haiku, minix, rtems, @@ -48,14 +71,230 @@ pub const Target = union(enum) { uefi, other, + /// See the documentation for `Os` for an explanation of the default version range. + pub fn defaultVersionRange(tag: @TagType(Os)) Os { + switch (tag) { + .freestanding => return .freestanding, + .ananas => return .ananas, + .cloudabi => return .cloudabi, + .dragonfly => return .dragonfly, + .freebsd => return .{ + .freebsd = Version.Range{ + .min = .{ .major = 12, .minor = 0 }, + .max = .{ .major = 12, .minor = 1 }, + }, + }, + .fuchsia => return .fuchsia, + .ios => return .ios, + .kfreebsd => return .kfreebsd, + .linux => return .{ + .linux = .{ + .range = .{ + .min = .{ .major = 3, .minor = 16 }, + .max = .{ .major = 5, .minor = 5, .patch = 5 }, + }, + .glibc = .{ .major = 2, .minor = 17 }, + }, + }, + .lv2 => return .lv2, + .macosx => return .{ + .min = .{ .major = 10, .minor = 13 }, + .max = .{ .major = 10, .minor = 15, .patch = 3 }, + }, + .netbsd => return .{ + .min = .{ .major = 8, .minor = 0 }, + .max = .{ .major = 9, .minor = 0 }, + }, + .openbsd => return .{ + .min = .{ .major = 6, .minor = 6 }, + .max = .{ .major = 6, .minor = 6 }, + }, + solaris => return .solaris, + windows => return .{ + .windows = .{ + .min = .win8_1, + .max = .win10_19h1, + }, + }, + haiku => return .haiku, + minix => return .minix, + rtems => return .rtems, + nacl => return .nacl, + cnk => return .cnk, + aix => return .aix, + cuda => return .cuda, + nvcl => return .nvcl, + amdhsa => return .amdhsa, + ps4 => return .ps4, + elfiamcu => return .elfiamcu, + tvos => return .tvos, + watchos => return .watchos, + mesa3d => return .mesa3d, + contiki => return .contiki, + amdpal => return .amdpal, + hermit => return .hermit, + hurd => return .hurd, + wasi => return .wasi, + emscripten => return .emscripten, + uefi => return .uefi, + other => return .other, + } + } + + pub const LinuxVersionRange = struct { + range: Version.Range, + glibc: Version, + + pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool { + return self.range.includesVersion(ver); + } + }; + + /// Based on NTDDI version constants from + /// https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt + pub const WindowsVersion = enum(u32) { + nt4 = 0x04000000, + win2k = 0x05000000, + xp = 0x05010000, + ws2003 = 0x05020000, + vista = 0x06000000, + win7 = 0x06010000, + win8 = 0x06020000, + win8_1 = 0x06030000, + win10 = 0x0A000000, + win10_th2 = 0x0A000001, + win10_rs1 = 0x0A000002, + win10_rs2 = 0x0A000003, + win10_rs3 = 0x0A000004, + win10_rs4 = 0x0A000005, + win10_rs5 = 0x0A000006, + win10_19h1 = 0x0A000007, + + pub const Range = struct { + min: WindowsVersion, + max: WindowsVersion, + + pub fn includesVersion(self: Range, ver: WindowsVersion) bool { + return @enumToInt(ver) >= @enumToInt(self.min) and @enumToInt(ver) <= @enumToInt(self.max); + } + }; + + pub fn nameToTag(name: []const u8) ?WindowsVersion { + const info = @typeInfo(WindowsVersion); + inline for (info.Enum.fields) |field| { + if (mem.eql(u8, name, field.name)) { + return @field(WindowsVersion, field.name); + } + } + return null; + } + }; + pub fn parse(text: []const u8) !Os { + var it = mem.separate(text, "."); + const os_name = it.next().?; + const tag = nameToTag(os_name) orelse return error.UnknownOperatingSystem; + const version_text = it.rest(); + const S = struct { + fn parseNone(s: []const u8) !void { + if (s.len != 0) return error.InvalidOperatingSystemVersion; + } + fn parseSemVer(s: []const u8, default: Version.Range) !Version.Range { + if (s.len == 0) return default; + var range_it = mem.separate(s, "..."); + + const min_text = range_it.next().?; + const min_ver = Version.parse(min_text) catch |err| switch (err) { + error.Overflow => return error.InvalidOperatingSystemVersion, + error.InvalidCharacter => return error.InvalidOperatingSystemVersion, + error.InvalidVersion => return error.InvalidOperatingSystemVersion, + }; + + const max_text = range_it.next() orelse return Version.Range{ + .min = min_ver, + .max = default.max, + }; + const max_ver = Version.parse(max_text) catch |err| switch (err) { + error.Overflow => return error.InvalidOperatingSystemVersion, + error.InvalidCharacter => return error.InvalidOperatingSystemVersion, + error.InvalidVersion => return error.InvalidOperatingSystemVersion, + }; + + return Version.Range{ .min = min_ver, .max = max_ver }; + } + fn parseWindows(s: []const u8, default: WindowsVersion.Range) !WindowsVersion.Range { + if (s.len == 0) return default; + var range_it = mem.separate(s, "..."); + + const min_text = range_it.next().?; + const min_ver = WindowsVersion.nameToTag(min_text) orelse + return error.InvalidOperatingSystemVersion; + + const max_text = range_it.next() orelse return WindowsVersion.Range{ + .min = min_ver, + .max = default.max, + }; + const max_ver = WindowsVersion.nameToTag(max_text) orelse + return error.InvalidOperatingSystemVersion; + + return WindowsVersion.Range{ .min = min_ver, .max = max_ver }; + } + }; + const default = defaultVersionRange(tag); + switch (tag) { + .freestanding => return Os{ .freestanding = try S.parseNone(version_text) }, + .ananas => return Os{ .ananas = try S.parseNone(version_text) }, + .cloudabi => return Os{ .cloudabi = try S.parseNone(version_text) }, + .dragonfly => return Os{ .dragonfly = try S.parseNone(version_text) }, + .freebsd => return Os{ .freebsd = try S.parseSemVer(version_text, default.freebsd) }, + .fuchsia => return Os{ .fuchsia = try S.parseNone(version_text) }, + .ios => return Os{ .ios = try S.parseNone(version_text) }, + .kfreebsd => return Os{ .kfreebsd = try S.parseNone(version_text) }, + .linux => return Os{ + .linux = .{ + .range = try S.parseSemVer(version_text, default.linux.range), + .glibc = default.linux.glibc, + }, + }, + .lv2 => return Os{ .lv2 = try S.parseNone(version_text) }, + .macosx => return Os{ .macosx = try S.parseSemVer(version_text, default.macosx) }, + .netbsd => return Os{ .netbsd = try S.parseSemVer(version_text, default.netbsd) }, + .openbsd => return Os{ .openbsd = try S.parseSemVer(version_text, default.openbsd) }, + .solaris => return Os{ .solaris = try S.parseNone(version_text) }, + .windows => return Os{ .windows = try S.parseWindows(version_text, default.windows) }, + .haiku => return Os{ .haiku = try S.parseNone(version_text) }, + .minix => return Os{ .minix = try S.parseNone(version_text) }, + .rtems => return Os{ .rtems = try S.parseNone(version_text) }, + .nacl => return Os{ .nacl = try S.parseNone(version_text) }, + .cnk => return Os{ .cnk = try S.parseNone(version_text) }, + .aix => return Os{ .aix = try S.parseNone(version_text) }, + .cuda => return Os{ .cuda = try S.parseNone(version_text) }, + .nvcl => return Os{ .nvcl = try S.parseNone(version_text) }, + .amdhsa => return Os{ .amdhsa = try S.parseNone(version_text) }, + .ps4 => return Os{ .ps4 = try S.parseNone(version_text) }, + .elfiamcu => return Os{ .elfiamcu = try S.parseNone(version_text) }, + .tvos => return Os{ .tvos = try S.parseNone(version_text) }, + .watchos => return Os{ .watchos = try S.parseNone(version_text) }, + .mesa3d => return Os{ .mesa3d = try S.parseNone(version_text) }, + .contiki => return Os{ .contiki = try S.parseNone(version_text) }, + .amdpal => return Os{ .amdpal = try S.parseNone(version_text) }, + .hermit => return Os{ .hermit = try S.parseNone(version_text) }, + .hurd => return Os{ .hurd = try S.parseNone(version_text) }, + .wasi => return Os{ .wasi = try S.parseNone(version_text) }, + .emscripten => return Os{ .emscripten = try S.parseNone(version_text) }, + .uefi => return Os{ .uefi = try S.parseNone(version_text) }, + .other => return Os{ .other = try S.parseNone(version_text) }, + } + } + + pub fn nameToTag(name: []const u8) ?@TagType(Os) { const info = @typeInfo(Os); - inline for (info.Enum.fields) |field| { - if (mem.eql(u8, text, field.name)) { + inline for (info.Union.fields) |field| { + if (mem.eql(u8, name, field.name)) { return @field(Os, field.name); } } - return error.UnknownOperatingSystem; + return null; } }; @@ -149,14 +388,39 @@ pub const Target = union(enum) { } } - pub fn parse(text: []const u8) !Abi { + pub fn nameToTag(text: []const u8) ?Abi { const info = @typeInfo(Abi); inline for (info.Enum.fields) |field| { if (mem.eql(u8, text, field.name)) { return @field(Abi, field.name); } } - return error.UnknownApplicationBinaryInterface; + return null; + } + + pub fn parse(text: []const u8, os: *Os) !Abi { + var it = mem.separate(text, "."); + const tag = nameToTag(it.next().?) orelse return error.UnknownApplicationBinaryInterface; + const version_text = it.rest(); + if (version_text.len != 0) { + if (@as(@TagType(Os), os.*) == .linux and tag.isGnu()) { + os.linux.glibc = Version.parse(version_text) catch |err| switch (err) { + error.Overflow => return error.InvalidGlibcVersion, + error.InvalidCharacter => return error.InvalidGlibcVersion, + error.InvalidVersion => return error.InvalidGlibcVersion, + }; + } else { + return error.InvalidAbiVersion; + } + } + return tag; + } + + pub fn isGnu(abi: Abi) bool { + return switch (abi) { + .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, + else => false, + }; } }; @@ -179,12 +443,6 @@ pub const Target = union(enum) { EfiRuntimeDriver, }; - pub const Cross = struct { - cpu: Cpu, - os: Os, - abi: Abi, - }; - pub const Cpu = struct { /// Architecture arch: Arch, @@ -641,20 +899,19 @@ pub const Target = union(enum) { }; pub const current = Target{ - .Cross = Cross{ - .cpu = builtin.cpu, - .os = builtin.os, - .abi = builtin.abi, - }, + .cpu = builtin.cpu, + .os = builtin.os, + .abi = builtin.abi, }; pub const stack_align = 16; + /// TODO add OS version ranges and glibc version pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ @tagName(self.getArch()), - @tagName(self.getOs()), - @tagName(self.getAbi()), + @tagName(self.os), + @tagName(self.abi), }); } @@ -678,7 +935,7 @@ pub const Target = union(enum) { else => return error.VcpkgNoSuchArchitecture, }; - const os = switch (target.getOs()) { + const os = switch (target.os) { .windows => "windows", .linux => "linux", .macosx => "macos", @@ -701,16 +958,16 @@ pub const Target = union(enum) { pub fn zigTripleNoSubArch(self: Target, allocator: *mem.Allocator) ![]u8 { return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ @tagName(self.getArch()), - @tagName(self.getOs()), - @tagName(self.getAbi()), + @tagName(self.os), + @tagName(self.abi), }); } pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ @tagName(self.getArch()), - @tagName(self.getOs()), - @tagName(self.getAbi()), + @tagName(self.os), + @tagName(self.abi), }); } @@ -760,11 +1017,11 @@ pub const Target = union(enum) { diags.arch = arch; const os_name = it.next() orelse return error.MissingOperatingSystem; - const os = try Os.parse(os_name); + var os = try Os.parse(os_name); // var because Abi.parse can update linux.glibc version diags.os = os; const abi_name = it.next(); - const abi = if (abi_name) |n| try Abi.parse(n) else Abi.default(arch, os); + const abi = if (abi_name) |n| try Abi.parse(n, &os) else Abi.default(arch, os); diags.abi = abi; if (it.next() != null) return error.UnexpectedExtraField; @@ -817,16 +1074,15 @@ pub const Target = union(enum) { .features = set, }; }; - var cross = Cross{ + return Target{ .cpu = cpu, .os = os, .abi = abi, }; - return Target{ .Cross = cross }; } pub fn oFileExt(self: Target) []const u8 { - return switch (self.getAbi()) { + return switch (self.abi) { .msvc => ".obj", else => ".o", }; @@ -848,7 +1104,7 @@ pub const Target = union(enum) { if (self.isWasm()) { return ".wasm"; } - switch (self.getAbi()) { + switch (self.abi) { .msvc => return ".lib", else => return ".a", } @@ -858,7 +1114,7 @@ pub const Target = union(enum) { if (self.isDarwin()) { return ".dylib"; } - switch (self.getOs()) { + switch (self.os) { .windows => return ".dll", else => return ".so", } @@ -868,52 +1124,41 @@ pub const Target = union(enum) { if (self.isWasm()) { return ""; } - switch (self.getAbi()) { + switch (self.abi) { .msvc => return "", else => return "lib", } } - pub fn getOs(self: Target) Os { - return switch (self) { - .Native => builtin.os, - .Cross => |t| t.os, - }; + /// Deprecated; access the `os` field directly. + pub fn getOs(self: Target) @TagType(Os) { + return self.os; } + /// Deprecated; access the `cpu` field directly. pub fn getCpu(self: Target) Cpu { - return switch (self) { - .Native => builtin.cpu, - .Cross => |cross| cross.cpu, - }; + return self.cpu; + } + + /// Deprecated; access the `abi` field directly. + pub fn getAbi(self: Target) Abi { + return self.abi; } pub fn getArch(self: Target) Cpu.Arch { - return self.getCpu().arch; - } - - pub fn getAbi(self: Target) Abi { - switch (self) { - .Native => return builtin.abi, - .Cross => |t| return t.abi, - } + return self.cpu.arch; } pub fn getObjectFormat(self: Target) ObjectFormat { - switch (self) { - .Native => return @import("builtin").object_format, - .Cross => blk: { - if (self.isWindows() or self.isUefi()) { - return .coff; - } else if (self.isDarwin()) { - return .macho; - } - if (self.isWasm()) { - return .wasm; - } - return .elf; - }, + if (self.isWindows() or self.isUefi()) { + return .coff; + } else if (self.isDarwin()) { + return .macho; } + if (self.isWasm()) { + return .wasm; + } + return .elf; } pub fn isMinGW(self: Target) bool { @@ -921,56 +1166,53 @@ pub const Target = union(enum) { } pub fn isGnu(self: Target) bool { - return switch (self.getAbi()) { - .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, - else => false, - }; + return self.abi.isGnu(); } pub fn isMusl(self: Target) bool { - return switch (self.getAbi()) { + return switch (self.abi) { .musl, .musleabi, .musleabihf => true, else => false, }; } pub fn isDarwin(self: Target) bool { - return switch (self.getOs()) { + return switch (self.os) { .ios, .macosx, .watchos, .tvos => true, else => false, }; } pub fn isWindows(self: Target) bool { - return switch (self.getOs()) { + return switch (self.os) { .windows => true, else => false, }; } pub fn isLinux(self: Target) bool { - return switch (self.getOs()) { + return switch (self.os) { .linux => true, else => false, }; } pub fn isAndroid(self: Target) bool { - return switch (self.getAbi()) { + return switch (self.abi) { .android => true, else => false, }; } pub fn isDragonFlyBSD(self: Target) bool { - return switch (self.getOs()) { + return switch (self.os) { .dragonfly => true, else => false, }; } pub fn isUefi(self: Target) bool { - return switch (self.getOs()) { + return switch (self.os) { .uefi => true, else => false, }; @@ -984,14 +1226,14 @@ pub const Target = union(enum) { } pub fn isFreeBSD(self: Target) bool { - return switch (self.getOs()) { + return switch (self.os) { .freebsd => true, else => false, }; } pub fn isNetBSD(self: Target) bool { - return switch (self.getOs()) { + return switch (self.os) { .netbsd => true, else => false, }; @@ -1081,7 +1323,7 @@ pub const Target = union(enum) { if (@as(@TagType(Target), self) == .Native) return .native; // If the target OS matches the host OS, we can use QEMU to emulate a foreign architecture. - if (self.getOs() == builtin.os) { + if (self.os == builtin.os) { return switch (self.getArch()) { .aarch64 => Executor{ .qemu = "qemu-aarch64" }, .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" }, @@ -1112,7 +1354,7 @@ pub const Target = union(enum) { } } - if (self.getOs() == .wasi) { + if (self.os == .wasi) { switch (self.getArchPtrBitWidth()) { 32 => return Executor{ .wasmtime = "wasmtime" }, else => return .unavailable, @@ -1129,7 +1371,7 @@ pub const Target = union(enum) { }; pub fn getFloatAbi(self: Target) FloatAbi { - return switch (self.getAbi()) { + return switch (self.abi) { .gnueabihf, .eabihf, .musleabihf, @@ -1145,7 +1387,7 @@ pub const Target = union(enum) { => return false, else => {}, } - switch (self.getOs()) { + switch (self.os) { .freestanding, .ios, .tvos, @@ -1200,7 +1442,7 @@ pub const Target = union(enum) { return result.toOwnedSlice(); } - switch (self.getOs()) { + switch (self.os) { .freebsd => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.1"), .netbsd => return mem.dupeZ(a, u8, "/libexec/ld.elf_so"), .dragonfly => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.2"), @@ -1233,7 +1475,7 @@ pub const Target = union(enum) { .powerpc64, .powerpc64le => return mem.dupeZ(a, u8, "/lib64/ld64.so.2"), .s390x => return mem.dupeZ(a, u8, "/lib64/ld64.so.1"), .sparcv9 => return mem.dupeZ(a, u8, "/lib64/ld-linux.so.2"), - .x86_64 => return mem.dupeZ(a, u8, switch (self.getAbi()) { + .x86_64 => return mem.dupeZ(a, u8, switch (self.abi) { .gnux32 => "/libx32/ld-linux-x32.so.2", else => "/lib64/ld-linux-x86-64.so.2", }), @@ -1292,10 +1534,10 @@ pub const Target = union(enum) { test "Target.parse" { { - const target = (try Target.parse(.{ + const target = try Target.parse(.{ .arch_os_abi = "x86_64-linux-gnu", .cpu_features = "x86_64-sse-sse2-avx-cx8", - })).Cross; + }); std.testing.expect(target.os == .linux); std.testing.expect(target.abi == .gnu); @@ -1307,10 +1549,10 @@ test "Target.parse" { std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr)); } { - const target = (try Target.parse(.{ + const target = try Target.parse(.{ .arch_os_abi = "arm-linux-musleabihf", .cpu_features = "generic+v8a", - })).Cross; + }); std.testing.expect(target.os == .linux); std.testing.expect(target.abi == .musleabihf); diff --git a/src/analyze.cpp b/src/analyze.cpp index 9b776da930..ceb232c79d 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3963,7 +3963,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) { // TODO more validation for types that can't be used for export/extern variables ZigType *implicit_type = nullptr; - if (explicit_type != nullptr && explicit_type->id == ZigTypeIdInvalid) { + if (explicit_type != nullptr && type_is_invalid(explicit_type)) { implicit_type = explicit_type; } else if (var_decl->expr) { init_value = analyze_const_value(g, tld_var->base.parent_scope, var_decl->expr, explicit_type, diff --git a/test/tests.zig b/test/tests.zig index e9fdde2e4b..60008a2bc6 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -56,7 +56,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.x86_64), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, @@ -65,7 +65,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.x86_64), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .gnu, }, }, @@ -75,7 +75,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.x86_64), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -86,7 +86,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.i386), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, @@ -95,7 +95,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.i386), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -106,7 +106,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.aarch64), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, @@ -115,7 +115,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.aarch64), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -125,7 +125,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.aarch64), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .gnu, }, }, @@ -158,7 +158,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.mipsel), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, @@ -167,7 +167,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.mipsel), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -178,7 +178,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.x86_64), - .os = .macosx, + .os = Target.Os.defaultVersionRange(.macosx), .abi = .gnu, }, }, @@ -190,7 +190,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.i386), - .os = .windows, + .os = Target.Os.defaultVersionRange(.windows), .abi = .msvc, }, }, @@ -200,7 +200,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.x86_64), - .os = .windows, + .os = Target.Os.defaultVersionRange(.windows), .abi = .msvc, }, }, @@ -210,7 +210,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.i386), - .os = .windows, + .os = Target.Os.defaultVersionRange(.windows), .abi = .gnu, }, }, @@ -221,7 +221,7 @@ const test_targets = blk: { .target = Target{ .Cross = CrossTarget{ .cpu = Target.Cpu.baseline(.x86_64), - .os = .windows, + .os = Target.Os.defaultVersionRange(.windows), .abi = .gnu, }, }, From 4616af0ca459358ffa09ba27f9daa8527a38fd35 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 25 Feb 2020 01:52:27 -0500 Subject: [PATCH 38/99] introduce operating system version ranges as part of the target * re-introduce `std.build.Target` which is distinct from `std.Target`. `std.build.Target` wraps `std.Target` so that it can be annotated as "the native target" or an explicitly specified target. * `std.Target.Os` is moved to `std.Target.Os.Tag`. The former is now a struct which has the tag as well as version range information. * `std.elf` gains some more ELF header constants. * `std.Target.parse` gains the ability to parse operating system version ranges as well as glibc version. * Added `std.Target.isGnuLibC()`. * self-hosted dynamic linker detection and glibc version detection. This also adds the improved logic using `/usr/bin/env` rather than invoking the system C compiler to find the dynamic linker when zig is statically linked. Related: #2084 Note: this `/usr/bin/env` code is work-in-progress. * `-target-glibc` CLI option is removed in favor of the new `-target` syntax. Example: `-target x86_64-linux-gnu.2.27` closes #1907 --- lib/std/build.zig | 40 +- lib/std/build/run.zig | 2 +- lib/std/builtin.zig | 4 +- lib/std/c.zig | 25 +- lib/std/c/linux.zig | 2 +- lib/std/child_process.zig | 26 +- lib/std/cstr.zig | 4 +- lib/std/debug.zig | 24 +- lib/std/dynamic_library.zig | 4 +- lib/std/elf.zig | 25 +- lib/std/event/channel.zig | 2 +- lib/std/event/future.zig | 2 +- lib/std/event/lock.zig | 2 +- lib/std/event/loop.zig | 26 +- lib/std/fs.zig | 46 +- lib/std/fs/file.zig | 12 +- lib/std/fs/get_app_data_dir.zig | 2 +- lib/std/fs/path.zig | 38 +- lib/std/fs/watch.zig | 8 +- lib/std/heap.zig | 14 +- lib/std/io.zig | 6 +- lib/std/mutex.zig | 4 +- lib/std/net.zig | 2 +- lib/std/net/test.zig | 4 +- lib/std/os.zig | 136 ++-- lib/std/os/bits.zig | 4 +- lib/std/os/linux.zig | 2 +- lib/std/os/test.zig | 16 +- lib/std/packed_int_array.zig | 4 +- lib/std/process.zig | 22 +- lib/std/reset_event.zig | 6 +- lib/std/special/c.zig | 8 +- lib/std/special/compiler_rt.zig | 13 +- .../special/compiler_rt/extendXfYf2_test.zig | 2 +- lib/std/spinlock.zig | 2 +- lib/std/start.zig | 16 +- lib/std/target.zig | 597 +++++++++--------- lib/std/thread.zig | 20 +- lib/std/time.zig | 18 +- lib/std/zig/system.zig | 330 +++++++++- src-self-hosted/c_int.zig | 2 +- src-self-hosted/clang.zig | 2 +- src-self-hosted/introspect.zig | 10 +- src-self-hosted/libc_installation.zig | 112 +--- src-self-hosted/link.zig | 4 +- src-self-hosted/main.zig | 6 +- src-self-hosted/print_targets.zig | 12 +- src-self-hosted/stage2.zig | 404 ++++++++---- src-self-hosted/util.zig | 22 - src/codegen.cpp | 38 +- src/error.cpp | 2 + src/main.cpp | 29 +- src/stage2.cpp | 8 - src/stage2.h | 10 +- test/stage1/behavior/asm.zig | 7 +- test/stage1/behavior/byteswap.zig | 5 +- .../namespace_depends_on_compile_var.zig | 8 +- test/stage1/behavior/vector.zig | 3 +- test/tests.zig | 2 +- 59 files changed, 1274 insertions(+), 932 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 29837d56d9..6c89e7b9d1 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -971,9 +971,9 @@ pub const Builder = struct { }; test "builder.findProgram compiles" { - // TODO: uncomment and fix the leak - // const builder = try Builder.create(std.testing.allocator, "zig", "zig-cache", "zig-cache"); - const builder = try Builder.create(std.heap.page_allocator, "zig", "zig-cache", "zig-cache"); + var buf: [1000]u8 = undefined; + var fba = std.heap.FixedBufferAllocator.init(&buf); + const builder = try Builder.create(&fba.allocator, "zig", "zig-cache", "zig-cache"); defer builder.destroy(); _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; } @@ -981,11 +981,37 @@ test "builder.findProgram compiles" { /// Deprecated. Use `builtin.Version`. pub const Version = builtin.Version; -/// Deprecated. Use `std.Target.Cross`. -pub const CrossTarget = std.Target.Cross; - /// Deprecated. Use `std.Target`. -pub const Target = std.Target; +pub const CrossTarget = std.Target; + +/// Wraps `std.Target` so that it can be annotated as "the native target" or an explicitly specified target. +pub const Target = union(enum) { + Native, + Cross: std.Target, + + pub fn getTarget(self: Target) std.Target { + return switch (self) { + .Native => std.Target.current, + .Cross => |t| t, + }; + } + + pub fn getOs(self: Target) std.Target.Os.Tag { + return self.getTarget().os.tag; + } + + pub fn getCpu(self: Target) std.Target.Cpu { + return self.getTarget().cpu; + } + + pub fn getAbi(self: Target) std.Target.Abi { + return self.getTarget().abi; + } + + pub fn getArch(self: Target) std.Target.Cpu.Arch { + return self.getCpu().arch; + } +}; pub const Pkg = struct { name: []const u8, diff --git a/lib/std/build/run.zig b/lib/std/build/run.zig index 75809bde03..eacf408ba9 100644 --- a/lib/std/build/run.zig +++ b/lib/std/build/run.zig @@ -82,7 +82,7 @@ pub const RunStep = struct { var key: []const u8 = undefined; var prev_path: ?[]const u8 = undefined; - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { key = "Path"; prev_path = env_map.get(key); if (prev_path == null) { diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 1bb8fd85ae..3204ce905e 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -411,7 +411,7 @@ pub const Version = struct { } }; - pub fn order(lhs: Version, rhs: version) std.math.Order { + pub fn order(lhs: Version, rhs: Version) std.math.Order { if (lhs.major < rhs.major) return .lt; if (lhs.major > rhs.major) return .gt; if (lhs.minor < rhs.minor) return .lt; @@ -504,7 +504,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn root.os.panic(msg, error_return_trace); unreachable; } - switch (os) { + switch (os.tag) { .freestanding => { while (true) { @breakpoint(); diff --git a/lib/std/c.zig b/lib/std/c.zig index f6c0e07dbd..9072d2c7cd 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -1,5 +1,5 @@ -const builtin = @import("builtin"); const std = @import("std"); +const builtin = std.builtin; const page_size = std.mem.page_size; pub const tokenizer = @import("c/tokenizer.zig"); @@ -10,7 +10,7 @@ pub const ast = @import("c/ast.zig"); pub usingnamespace @import("os/bits.zig"); -pub usingnamespace switch (builtin.os) { +pub usingnamespace switch (std.Target.current.os.tag) { .linux => @import("c/linux.zig"), .windows => @import("c/windows.zig"), .macosx, .ios, .tvos, .watchos => @import("c/darwin.zig"), @@ -46,17 +46,16 @@ pub fn versionCheck(glibc_version: builtin.Version) type { return struct { pub const ok = blk: { if (!builtin.link_libc) break :blk false; - switch (builtin.abi) { - .musl, .musleabi, .musleabihf => break :blk true, - .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => { - const ver = builtin.glibc_version orelse break :blk false; - if (ver.major < glibc_version.major) break :blk false; - if (ver.major > glibc_version.major) break :blk true; - if (ver.minor < glibc_version.minor) break :blk false; - if (ver.minor > glibc_version.minor) break :blk true; - break :blk ver.patch >= glibc_version.patch; - }, - else => break :blk false, + if (std.Target.current.abi.isMusl()) break :blk true; + if (std.Target.current.isGnuLibC()) { + const ver = std.Target.current.os.version_range.linux.glibc; + const order = ver.order(glibc_version); + break :blk switch (order) { + .gt, .eq => true, + .lt => false, + }; + } else { + break :blk false; } }; }; diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig index 0f7abaaaa0..be32536d6f 100644 --- a/lib/std/c/linux.zig +++ b/lib/std/c/linux.zig @@ -94,7 +94,7 @@ pub const pthread_cond_t = extern struct { size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, }; const __SIZEOF_PTHREAD_COND_T = 48; -const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os == .fuchsia) 40 else switch (builtin.abi) { +const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os.tag == .fuchsia) 40 else switch (builtin.abi) { .musl, .musleabi, .musleabihf => if (@sizeOf(usize) == 8) 40 else 24, .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (builtin.arch) { .aarch64 => 48, diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index bb8ed2e8a0..d5e914b286 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -17,9 +17,9 @@ const TailQueue = std.TailQueue; const maxInt = std.math.maxInt; pub const ChildProcess = struct { - pid: if (builtin.os == .windows) void else i32, - handle: if (builtin.os == .windows) windows.HANDLE else void, - thread_handle: if (builtin.os == .windows) windows.HANDLE else void, + pid: if (builtin.os.tag == .windows) void else i32, + handle: if (builtin.os.tag == .windows) windows.HANDLE else void, + thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void, allocator: *mem.Allocator, @@ -39,15 +39,15 @@ pub const ChildProcess = struct { stderr_behavior: StdIo, /// Set to change the user id when spawning the child process. - uid: if (builtin.os == .windows) void else ?u32, + uid: if (builtin.os.tag == .windows) void else ?u32, /// Set to change the group id when spawning the child process. - gid: if (builtin.os == .windows) void else ?u32, + gid: if (builtin.os.tag == .windows) void else ?u32, /// Set to change the current working directory when spawning the child process. cwd: ?[]const u8, - err_pipe: if (builtin.os == .windows) void else [2]os.fd_t, + err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t, expand_arg0: Arg0Expand, @@ -96,8 +96,8 @@ pub const ChildProcess = struct { .term = null, .env_map = null, .cwd = null, - .uid = if (builtin.os == .windows) {} else null, - .gid = if (builtin.os == .windows) {} else null, + .uid = if (builtin.os.tag == .windows) {} else null, + .gid = if (builtin.os.tag == .windows) {} else null, .stdin = null, .stdout = null, .stderr = null, @@ -118,7 +118,7 @@ pub const ChildProcess = struct { /// On success must call `kill` or `wait`. pub fn spawn(self: *ChildProcess) SpawnError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return self.spawnWindows(); } else { return self.spawnPosix(); @@ -132,7 +132,7 @@ pub const ChildProcess = struct { /// Forcibly terminates child process and then cleans up all resources. pub fn kill(self: *ChildProcess) !Term { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return self.killWindows(1); } else { return self.killPosix(); @@ -162,7 +162,7 @@ pub const ChildProcess = struct { /// Blocks until child process terminates and then cleans up all resources. pub fn wait(self: *ChildProcess) !Term { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return self.waitWindows(); } else { return self.waitPosix(); @@ -307,7 +307,7 @@ pub const ChildProcess = struct { fn cleanupAfterWait(self: *ChildProcess, status: u32) !Term { defer destroyPipe(self.err_pipe); - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { var fd = [1]std.os.pollfd{std.os.pollfd{ .fd = self.err_pipe[0], .events = std.os.POLLIN, @@ -402,7 +402,7 @@ pub const ChildProcess = struct { // This pipe is used to communicate errors between the time of fork // and execve from the child process to the parent process. const err_pipe = blk: { - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { const fd = try os.eventfd(0, 0); // There's no distinction between the readable and the writeable // end with eventfd diff --git a/lib/std/cstr.zig b/lib/std/cstr.zig index 765e0a45cf..4057d4b62b 100644 --- a/lib/std/cstr.zig +++ b/lib/std/cstr.zig @@ -4,8 +4,8 @@ const debug = std.debug; const mem = std.mem; const testing = std.testing; -pub const line_sep = switch (builtin.os) { - builtin.Os.windows => "\r\n", +pub const line_sep = switch (builtin.os.tag) { + .windows => "\r\n", else => "\n", }; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 343df9bde0..558b7e0513 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1,4 +1,5 @@ const std = @import("std.zig"); +const builtin = std.builtin; const math = std.math; const mem = std.mem; const io = std.io; @@ -11,7 +12,6 @@ const macho = std.macho; const coff = std.coff; const pdb = std.pdb; const ArrayList = std.ArrayList; -const builtin = @import("builtin"); const root = @import("root"); const maxInt = std.math.maxInt; const File = std.fs.File; @@ -101,7 +101,7 @@ pub fn detectTTYConfig() TTY.Config { } else |_| { if (stderr_file.supportsAnsiEscapeCodes()) { return .escape_codes; - } else if (builtin.os == .windows and stderr_file.isTty()) { + } else if (builtin.os.tag == .windows and stderr_file.isTty()) { return .windows_api; } else { return .no_color; @@ -155,7 +155,7 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { /// chopping off the irrelevant frames and shifting so that the returned addresses pointer /// equals the passed in addresses pointer. pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace) void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const addrs = stack_trace.instruction_addresses; const u32_addrs_len = @intCast(u32, addrs.len); const first_addr = first_address orelse { @@ -231,7 +231,7 @@ pub fn assert(ok: bool) void { pub fn panic(comptime format: []const u8, args: var) noreturn { @setCold(true); // TODO: remove conditional once wasi / LLVM defines __builtin_return_address - const first_trace_addr = if (builtin.os == .wasi) null else @returnAddress(); + const first_trace_addr = if (builtin.os.tag == .wasi) null else @returnAddress(); panicExtra(null, first_trace_addr, format, args); } @@ -361,7 +361,7 @@ pub fn writeCurrentStackTrace( tty_config: TTY.Config, start_addr: ?usize, ) !void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return writeCurrentStackTraceWindows(out_stream, debug_info, tty_config, start_addr); } var it = StackIterator.init(start_addr, null); @@ -418,7 +418,7 @@ pub const TTY = struct { .Dim => noasync out_stream.write(DIM) catch return, .Reset => noasync out_stream.write(RESET) catch return, }, - .windows_api => if (builtin.os == .windows) { + .windows_api => if (builtin.os.tag == .windows) { const S = struct { var attrs: windows.WORD = undefined; var init_attrs = false; @@ -617,7 +617,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) { return noasync root.os.debug.openSelfDebugInfo(allocator); } - switch (builtin.os) { + switch (builtin.os.tag) { .linux, .freebsd, .macosx, @@ -1019,7 +1019,7 @@ pub const DebugInfo = struct { pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo { if (comptime std.Target.current.isDarwin()) return self.lookupModuleDyld(address) - else if (builtin.os == .windows) + else if (builtin.os.tag == .windows) return self.lookupModuleWin32(address) else return self.lookupModuleDl(address); @@ -1242,7 +1242,7 @@ const SymbolInfo = struct { } }; -pub const ModuleDebugInfo = switch (builtin.os) { +pub const ModuleDebugInfo = switch (builtin.os.tag) { .macosx, .ios, .watchos, .tvos => struct { base_address: usize, mapped_memory: []const u8, @@ -1602,7 +1602,7 @@ fn getDebugInfoAllocator() *mem.Allocator { } /// Whether or not the current target can print useful debug information when a segfault occurs. -pub const have_segfault_handling_support = builtin.os == .linux or builtin.os == .windows; +pub const have_segfault_handling_support = builtin.os.tag == .linux or builtin.os.tag == .windows; pub const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler")) root.enable_segfault_handler else @@ -1621,7 +1621,7 @@ pub fn attachSegfaultHandler() void { if (!have_segfault_handling_support) { @compileError("segfault handler not supported for this target"); } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); return; } @@ -1637,7 +1637,7 @@ pub fn attachSegfaultHandler() void { } fn resetSegfaultHandler() void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { if (windows_segfault_handle) |handle| { assert(windows.kernel32.RemoveVectoredExceptionHandler(handle) != 0); windows_segfault_handle = null; diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index 409dace20f..34f45894fb 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -11,7 +11,7 @@ const system = std.os.system; const maxInt = std.math.maxInt; const max = std.math.max; -pub const DynLib = switch (builtin.os) { +pub const DynLib = switch (builtin.os.tag) { .linux => if (builtin.link_libc) DlDynlib else ElfDynLib, .windows => WindowsDynLib, .macosx, .tvos, .watchos, .ios, .freebsd => DlDynlib, @@ -390,7 +390,7 @@ pub const DlDynlib = struct { }; test "dynamic_library" { - const libname = switch (builtin.os) { + const libname = switch (builtin.os.tag) { .linux, .freebsd => "invalid_so.so", .windows => "invalid_dll.dll", .macosx, .tvos, .watchos, .ios => "invalid_dylib.dylib", diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 9e4c1ac5f6..007a01bb90 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -349,16 +349,6 @@ pub const Elf = struct { program_headers: []ProgramHeader, allocator: *mem.Allocator, - /// Call close when done. - pub fn openPath(allocator: *mem.Allocator, path: []const u8) !Elf { - @compileError("TODO implement"); - } - - /// Call close when done. - pub fn openFile(allocator: *mem.Allocator, file: File) !Elf { - @compileError("TODO implement"); - } - pub fn openStream( allocator: *mem.Allocator, seekable_stream: *io.SeekableStream(anyerror, anyerror), @@ -554,6 +544,21 @@ pub const Elf = struct { }; pub const EI_NIDENT = 16; + +pub const EI_CLASS = 4; +pub const ELFCLASSNONE = 0; +pub const ELFCLASS32 = 1; +pub const ELFCLASS64 = 2; +pub const ELFCLASSNUM = 3; + +pub const EI_DATA = 5; +pub const ELFDATANONE = 0; +pub const ELFDATA2LSB = 1; +pub const ELFDATA2MSB = 2; +pub const ELFDATANUM = 3; + +pub const EI_VERSION = 6; + pub const Elf32_Half = u16; pub const Elf64_Half = u16; pub const Elf32_Word = u32; diff --git a/lib/std/event/channel.zig b/lib/std/event/channel.zig index fd70f73aab..3c5b48d047 100644 --- a/lib/std/event/channel.zig +++ b/lib/std/event/channel.zig @@ -273,7 +273,7 @@ test "std.event.Channel" { if (builtin.single_threaded) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/3251 - if (builtin.os == .freebsd) return error.SkipZigTest; + if (builtin.os.tag == .freebsd) return error.SkipZigTest; var channel: Channel(i32) = undefined; channel.init(&[0]i32{}); diff --git a/lib/std/event/future.zig b/lib/std/event/future.zig index 492582da75..51a63e90ee 100644 --- a/lib/std/event/future.zig +++ b/lib/std/event/future.zig @@ -86,7 +86,7 @@ test "std.event.Future" { // https://github.com/ziglang/zig/issues/1908 if (builtin.single_threaded) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/3251 - if (builtin.os == .freebsd) return error.SkipZigTest; + if (builtin.os.tag == .freebsd) return error.SkipZigTest; // TODO provide a way to run tests in evented I/O mode if (!std.io.is_async) return error.SkipZigTest; diff --git a/lib/std/event/lock.zig b/lib/std/event/lock.zig index e1b3495e5c..b9cbb5d95f 100644 --- a/lib/std/event/lock.zig +++ b/lib/std/event/lock.zig @@ -123,7 +123,7 @@ test "std.event.Lock" { if (builtin.single_threaded) return error.SkipZigTest; // TODO https://github.com/ziglang/zig/issues/3251 - if (builtin.os == .freebsd) return error.SkipZigTest; + if (builtin.os.tag == .freebsd) return error.SkipZigTest; var lock = Lock.init(); defer lock.deinit(); diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig index 085e56fc15..80ba5a79b5 100644 --- a/lib/std/event/loop.zig +++ b/lib/std/event/loop.zig @@ -34,7 +34,7 @@ pub const Loop = struct { handle: anyframe, overlapped: Overlapped, - pub const overlapped_init = switch (builtin.os) { + pub const overlapped_init = switch (builtin.os.tag) { .windows => windows.OVERLAPPED{ .Internal = 0, .InternalHigh = 0, @@ -52,7 +52,7 @@ pub const Loop = struct { EventFd, }; - pub const EventFd = switch (builtin.os) { + pub const EventFd = switch (builtin.os.tag) { .macosx, .freebsd, .netbsd, .dragonfly => KEventFd, .linux => struct { base: ResumeNode, @@ -71,7 +71,7 @@ pub const Loop = struct { kevent: os.Kevent, }; - pub const Basic = switch (builtin.os) { + pub const Basic = switch (builtin.os.tag) { .macosx, .freebsd, .netbsd, .dragonfly => KEventBasic, .linux => struct { base: ResumeNode, @@ -173,7 +173,7 @@ pub const Loop = struct { const wakeup_bytes = [_]u8{0x1} ** 8; fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void { - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { self.os_data.fs_queue = std.atomic.Queue(Request).init(); self.os_data.fs_queue_item = 0; @@ -404,7 +404,7 @@ pub const Loop = struct { } fn deinitOsData(self: *Loop) void { - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { noasync os.close(self.os_data.final_eventfd); while (self.available_eventfd_resume_nodes.pop()) |node| noasync os.close(node.data.eventfd); @@ -568,7 +568,7 @@ pub const Loop = struct { }; const eventfd_node = &resume_stack_node.data; eventfd_node.base.handle = next_tick_node.data; - switch (builtin.os) { + switch (builtin.os.tag) { .macosx, .freebsd, .netbsd, .dragonfly => { const kevent_array = @as(*const [1]os.Kevent, &eventfd_node.kevent); const empty_kevs = &[0]os.Kevent{}; @@ -628,7 +628,7 @@ pub const Loop = struct { self.workerRun(); - switch (builtin.os) { + switch (builtin.os.tag) { .linux, .macosx, .freebsd, @@ -678,7 +678,7 @@ pub const Loop = struct { const prev = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); if (prev == 1) { // cause all the threads to stop - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { self.posixFsRequest(&self.os_data.fs_end_request); // writing 8 bytes to an eventfd cannot fail @@ -902,7 +902,7 @@ pub const Loop = struct { self.finishOneEvent(); } - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { // only process 1 event so we don't steal from other threads var events: [1]os.linux.epoll_event = undefined; @@ -989,7 +989,7 @@ pub const Loop = struct { fn posixFsRequest(self: *Loop, request_node: *Request.Node) void { self.beginOneEvent(); // finished in posixFsRun after processing the msg self.os_data.fs_queue.put(request_node); - switch (builtin.os) { + switch (builtin.os.tag) { .macosx, .freebsd, .netbsd, .dragonfly => { const fs_kevs = @as(*const [1]os.Kevent, &self.os_data.fs_kevent_wake); const empty_kevs = &[0]os.Kevent{}; @@ -1018,7 +1018,7 @@ pub const Loop = struct { // https://github.com/ziglang/zig/issues/3157 fn posixFsRun(self: *Loop) void { while (true) { - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { @atomicStore(i32, &self.os_data.fs_queue_item, 0, .SeqCst); } while (self.os_data.fs_queue.get()) |node| { @@ -1053,7 +1053,7 @@ pub const Loop = struct { } self.finishOneEvent(); } - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { const rc = os.linux.futex_wait(&self.os_data.fs_queue_item, os.linux.FUTEX_WAIT, 0, null); switch (os.linux.getErrno(rc)) { @@ -1071,7 +1071,7 @@ pub const Loop = struct { } } - const OsData = switch (builtin.os) { + const OsData = switch (builtin.os.tag) { .linux => LinuxOsData, .macosx, .freebsd, .netbsd, .dragonfly => KEventData, .windows => struct { diff --git a/lib/std/fs.zig b/lib/std/fs.zig index ecd2c2b750..5077c52cd9 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -29,7 +29,7 @@ pub const Watch = @import("fs/watch.zig").Watch; /// All file system operations which return a path are guaranteed to /// fit into a UTF-8 encoded array of this length. /// The byte count includes room for a null sentinel byte. -pub const MAX_PATH_BYTES = switch (builtin.os) { +pub const MAX_PATH_BYTES = switch (builtin.os.tag) { .linux, .macosx, .ios, .freebsd, .netbsd, .dragonfly => os.PATH_MAX, // Each UTF-16LE character may be expanded to 3 UTF-8 bytes. // If it would require 4 UTF-8 bytes, then there would be a surrogate @@ -47,7 +47,7 @@ pub const base64_encoder = base64.Base64Encoder.init( /// Whether or not async file system syscalls need a dedicated thread because the operating /// system does not support non-blocking I/O on the file system. -pub const need_async_thread = std.io.is_async and switch (builtin.os) { +pub const need_async_thread = std.io.is_async and switch (builtin.os.tag) { .windows, .other => false, else => true, }; @@ -270,7 +270,7 @@ pub const AtomicFile = struct { assert(!self.finished); self.file.close(); self.finished = true; - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_path); const tmp_path_w = try os.windows.cStrToPrefixedFileW(@ptrCast([*:0]u8, &self.tmp_path_buf)); return os.renameW(&tmp_path_w, &dest_path_w); @@ -394,7 +394,7 @@ pub const Dir = struct { const IteratorError = error{AccessDenied} || os.UnexpectedError; - pub const Iterator = switch (builtin.os) { + pub const Iterator = switch (builtin.os.tag) { .macosx, .ios, .freebsd, .netbsd, .dragonfly => struct { dir: Dir, seek: i64, @@ -409,7 +409,7 @@ pub const Dir = struct { /// Memory such as file names referenced in this returned entry becomes invalid /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. pub fn next(self: *Self) Error!?Entry { - switch (builtin.os) { + switch (builtin.os.tag) { .macosx, .ios => return self.nextDarwin(), .freebsd, .netbsd, .dragonfly => return self.nextBsd(), else => @compileError("unimplemented"), @@ -644,7 +644,7 @@ pub const Dir = struct { }; pub fn iterate(self: Dir) Iterator { - switch (builtin.os) { + switch (builtin.os.tag) { .macosx, .ios, .freebsd, .netbsd, .dragonfly => return Iterator{ .dir = self, .seek = 0, @@ -710,7 +710,7 @@ pub const Dir = struct { /// Asserts that the path parameter has no null bytes. pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.openFileW(&path_w, flags); } @@ -720,7 +720,7 @@ pub const Dir = struct { /// Same as `openFile` but the path parameter is null-terminated. pub fn openFileC(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const path_w = try os.windows.cStrToPrefixedFileW(sub_path); return self.openFileW(&path_w, flags); } @@ -760,7 +760,7 @@ pub const Dir = struct { /// Asserts that the path parameter has no null bytes. pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.createFileW(&path_w, flags); } @@ -770,7 +770,7 @@ pub const Dir = struct { /// Same as `createFile` but the path parameter is null-terminated. pub fn createFileC(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); return self.createFileW(&path_w, flags); } @@ -901,7 +901,7 @@ pub const Dir = struct { /// Asserts that the path parameter has no null bytes. pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir { if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.openDirTraverseW(&sub_path_w); } @@ -919,7 +919,7 @@ pub const Dir = struct { /// Asserts that the path parameter has no null bytes. pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir { if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.openDirListW(&sub_path_w); } @@ -930,7 +930,7 @@ pub const Dir = struct { /// Same as `openDirTraverse` except the parameter is null-terminated. pub fn openDirTraverseC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); return self.openDirTraverseW(&sub_path_w); } else { @@ -941,7 +941,7 @@ pub const Dir = struct { /// Same as `openDirList` except the parameter is null-terminated. pub fn openDirListC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); return self.openDirListW(&sub_path_w); } else { @@ -1083,7 +1083,7 @@ pub const Dir = struct { /// Asserts that the path parameter has no null bytes. pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.deleteDirW(&sub_path_w); } @@ -1340,7 +1340,7 @@ pub const Dir = struct { /// For example, instead of testing if a file exists and then opening it, just /// open it and handle the error for file not found. pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.accessW(&sub_path_w, flags); } @@ -1350,7 +1350,7 @@ pub const Dir = struct { /// Same as `access` except the path parameter is null-terminated. pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path); return self.accessW(&sub_path_w, flags); } @@ -1381,7 +1381,7 @@ pub const Dir = struct { /// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior. /// On POSIX targets, this function is comptime-callable. pub fn cwd() Dir { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle }; } else { return Dir{ .fd = os.AT_FDCWD }; @@ -1560,10 +1560,10 @@ pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError; pub fn openSelfExe() OpenSelfExeError!File { - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { return openFileAbsoluteC("/proc/self/exe", .{}); } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const wide_slice = selfExePathW(); const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice); return cwd().openReadW(&prefixed_path_w); @@ -1575,7 +1575,7 @@ pub fn openSelfExe() OpenSelfExeError!File { } test "openSelfExe" { - switch (builtin.os) { + switch (builtin.os.tag) { .linux, .macosx, .ios, .windows, .freebsd, .dragonfly => (try openSelfExe()).close(), else => return error.SkipZigTest, // Unsupported OS. } @@ -1600,7 +1600,7 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 { if (rc != 0) return error.NameTooLong; return mem.toSlice(u8, @ptrCast([*:0]u8, out_buffer)); } - switch (builtin.os) { + switch (builtin.os.tag) { .linux => return os.readlinkC("/proc/self/exe", out_buffer), .freebsd, .dragonfly => { var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC, os.KERN_PROC_PATHNAME, -1 }; @@ -1642,7 +1642,7 @@ pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 { /// Get the directory path that contains the current executable. /// Returned value is a slice of out_buffer. pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const u8 { - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { // If the currently executing binary has been deleted, // the file path looks something like `/a/b/c/exe (deleted)` // This path cannot be opened, but it's valid for determining the directory diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 9d0ffcfd4a..c243eeb62c 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -29,7 +29,7 @@ pub const File = struct { pub const Mode = os.mode_t; - pub const default_mode = switch (builtin.os) { + pub const default_mode = switch (builtin.os.tag) { .windows => 0, else => 0o666, }; @@ -83,7 +83,7 @@ pub const File = struct { /// Test whether ANSI escape codes will be treated as such. pub fn supportsAnsiEscapeCodes(self: File) bool { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return os.isCygwinPty(self.handle); } if (self.isTty()) { @@ -128,7 +128,7 @@ pub const File = struct { /// TODO: integrate with async I/O pub fn getEndPos(self: File) GetPosError!u64 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.GetFileSizeEx(self.handle); } return (try self.stat()).size; @@ -138,7 +138,7 @@ pub const File = struct { /// TODO: integrate with async I/O pub fn mode(self: File) ModeError!Mode { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return {}; } return (try self.stat()).mode; @@ -162,7 +162,7 @@ pub const File = struct { /// TODO: integrate with async I/O pub fn stat(self: File) StatError!Stat { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { var io_status_block: windows.IO_STATUS_BLOCK = undefined; var info: windows.FILE_ALL_INFORMATION = undefined; const rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &info, @sizeOf(windows.FILE_ALL_INFORMATION), .FileAllInformation); @@ -209,7 +209,7 @@ pub const File = struct { /// last modification timestamp in nanoseconds mtime: i64, ) UpdateTimesError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const atime_ft = windows.nanoSecondsToFileTime(atime); const mtime_ft = windows.nanoSecondsToFileTime(mtime); return windows.SetFileTime(self.handle, null, &atime_ft, &mtime_ft); diff --git a/lib/std/fs/get_app_data_dir.zig b/lib/std/fs/get_app_data_dir.zig index 35c0265435..31aab590d8 100644 --- a/lib/std/fs/get_app_data_dir.zig +++ b/lib/std/fs/get_app_data_dir.zig @@ -13,7 +13,7 @@ pub const GetAppDataDirError = error{ /// Caller owns returned memory. /// TODO determine if we can remove the allocator requirement pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { - switch (builtin.os) { + switch (builtin.os.tag) { .windows => { var dir_path_ptr: [*:0]u16 = undefined; switch (os.windows.shell32.SHGetKnownFolderPath( diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index 5d1c775629..35bc9b53b0 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -13,18 +13,18 @@ const process = std.process; pub const sep_windows = '\\'; pub const sep_posix = '/'; -pub const sep = if (builtin.os == .windows) sep_windows else sep_posix; +pub const sep = if (builtin.os.tag == .windows) sep_windows else sep_posix; pub const sep_str_windows = "\\"; pub const sep_str_posix = "/"; -pub const sep_str = if (builtin.os == .windows) sep_str_windows else sep_str_posix; +pub const sep_str = if (builtin.os.tag == .windows) sep_str_windows else sep_str_posix; pub const delimiter_windows = ';'; pub const delimiter_posix = ':'; -pub const delimiter = if (builtin.os == .windows) delimiter_windows else delimiter_posix; +pub const delimiter = if (builtin.os.tag == .windows) delimiter_windows else delimiter_posix; pub fn isSep(byte: u8) bool { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return byte == '/' or byte == '\\'; } else { return byte == '/'; @@ -74,7 +74,7 @@ fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u return buf; } -pub const join = if (builtin.os == .windows) joinWindows else joinPosix; +pub const join = if (builtin.os.tag == .windows) joinWindows else joinPosix; /// Naively combines a series of paths with the native path seperator. /// Allocates memory for the result, which must be freed by the caller. @@ -129,7 +129,7 @@ test "join" { } pub fn isAbsoluteC(path_c: [*:0]const u8) bool { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return isAbsoluteWindowsC(path_c); } else { return isAbsolutePosixC(path_c); @@ -137,7 +137,7 @@ pub fn isAbsoluteC(path_c: [*:0]const u8) bool { } pub fn isAbsolute(path: []const u8) bool { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return isAbsoluteWindows(path); } else { return isAbsolutePosix(path); @@ -318,7 +318,7 @@ test "windowsParsePath" { } pub fn diskDesignator(path: []const u8) []const u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return diskDesignatorWindows(path); } else { return ""; @@ -383,7 +383,7 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool { /// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`. pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return resolveWindows(allocator, paths); } else { return resolvePosix(allocator, paths); @@ -400,7 +400,7 @@ pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 { /// Without performing actual syscalls, resolving `..` could be incorrect. pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { - assert(builtin.os == .windows); // resolveWindows called on non windows can't use getCwd + assert(builtin.os.tag == .windows); // resolveWindows called on non windows can't use getCwd return process.getCwdAlloc(allocator); } @@ -495,7 +495,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { result_disk_designator = result[0..result_index]; }, WindowsPath.Kind.None => { - assert(builtin.os == .windows); // resolveWindows called on non windows can't use getCwd + assert(builtin.os.tag == .windows); // resolveWindows called on non windows can't use getCwd const cwd = try process.getCwdAlloc(allocator); defer allocator.free(cwd); const parsed_cwd = windowsParsePath(cwd); @@ -510,7 +510,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { }, } } else { - assert(builtin.os == .windows); // resolveWindows called on non windows can't use getCwd + assert(builtin.os.tag == .windows); // resolveWindows called on non windows can't use getCwd // TODO call get cwd for the result_disk_designator instead of the global one const cwd = try process.getCwdAlloc(allocator); defer allocator.free(cwd); @@ -581,7 +581,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { /// Without performing actual syscalls, resolving `..` could be incorrect. pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { - assert(builtin.os != .windows); // resolvePosix called on windows can't use getCwd + assert(builtin.os.tag != .windows); // resolvePosix called on windows can't use getCwd return process.getCwdAlloc(allocator); } @@ -603,7 +603,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (have_abs) { result = try allocator.alloc(u8, max_size); } else { - assert(builtin.os != .windows); // resolvePosix called on windows can't use getCwd + assert(builtin.os.tag != .windows); // resolvePosix called on windows can't use getCwd const cwd = try process.getCwdAlloc(allocator); defer allocator.free(cwd); result = try allocator.alloc(u8, max_size + cwd.len + 1); @@ -645,7 +645,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { test "resolve" { const cwd = try process.getCwdAlloc(testing.allocator); defer testing.allocator.free(cwd); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) { cwd[0] = asciiUpper(cwd[0]); } @@ -661,7 +661,7 @@ test "resolveWindows" { // TODO https://github.com/ziglang/zig/issues/3288 return error.SkipZigTest; } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const cwd = try process.getCwdAlloc(testing.allocator); defer testing.allocator.free(cwd); const parsed_cwd = windowsParsePath(cwd); @@ -732,7 +732,7 @@ fn testResolvePosix(paths: []const []const u8, expected: []const u8) !void { /// If the path is a file in the current directory (no directory component) /// then returns null pub fn dirname(path: []const u8) ?[]const u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return dirnameWindows(path); } else { return dirnamePosix(path); @@ -864,7 +864,7 @@ fn testDirnameWindows(input: []const u8, expected_output: ?[]const u8) void { } pub fn basename(path: []const u8) []const u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return basenameWindows(path); } else { return basenamePosix(path); @@ -980,7 +980,7 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) void { /// string is returned. /// On Windows this canonicalizes the drive to a capital letter and paths to `\\`. pub fn relative(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return relativeWindows(allocator, from, to); } else { return relativePosix(allocator, from, to); diff --git a/lib/std/fs/watch.zig b/lib/std/fs/watch.zig index 0ff8c47ecf..1eb5a97ff1 100644 --- a/lib/std/fs/watch.zig +++ b/lib/std/fs/watch.zig @@ -42,7 +42,7 @@ pub fn Watch(comptime V: type) type { os_data: OsData, allocator: *Allocator, - const OsData = switch (builtin.os) { + const OsData = switch (builtin.os.tag) { // TODO https://github.com/ziglang/zig/issues/3778 .macosx, .freebsd, .netbsd, .dragonfly => KqOsData, .linux => LinuxOsData, @@ -121,7 +121,7 @@ pub fn Watch(comptime V: type) type { const self = try allocator.create(Self); errdefer allocator.destroy(self); - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC); errdefer os.close(inotify_fd); @@ -172,7 +172,7 @@ pub fn Watch(comptime V: type) type { /// All addFile calls and removeFile calls must have completed. pub fn deinit(self: *Self) void { - switch (builtin.os) { + switch (builtin.os.tag) { .macosx, .freebsd, .netbsd, .dragonfly => { // TODO we need to cancel the frames before destroying the lock self.os_data.table_lock.deinit(); @@ -223,7 +223,7 @@ pub fn Watch(comptime V: type) type { } pub fn addFile(self: *Self, file_path: []const u8, value: V) !?V { - switch (builtin.os) { + switch (builtin.os.tag) { .macosx, .freebsd, .netbsd, .dragonfly => return addFileKEvent(self, file_path, value), .linux => return addFileLinux(self, file_path, value), .windows => return addFileWindows(self, file_path, value), diff --git a/lib/std/heap.zig b/lib/std/heap.zig index 4295f1393d..65809e97b4 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -36,7 +36,7 @@ fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new /// Thread-safe and lock-free. pub const page_allocator = if (std.Target.current.isWasm()) &wasm_page_allocator_state -else if (std.Target.current.getOs() == .freestanding) +else if (std.Target.current.os.tag == .freestanding) root.os.heap.page_allocator else &page_allocator_state; @@ -57,7 +57,7 @@ const PageAllocator = struct { fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 { if (n == 0) return &[0]u8{}; - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const w = os.windows; // Although officially it's at least aligned to page boundary, @@ -143,7 +143,7 @@ const PageAllocator = struct { fn shrink(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { const old_mem = @alignCast(mem.page_size, old_mem_unaligned); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const w = os.windows; if (new_size == 0) { // From the docs: @@ -183,7 +183,7 @@ const PageAllocator = struct { fn realloc(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { const old_mem = @alignCast(mem.page_size, old_mem_unaligned); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { if (old_mem.len == 0) { return alloc(allocator, new_size, new_align); } @@ -412,7 +412,7 @@ const WasmPageAllocator = struct { } }; -pub const HeapAllocator = switch (builtin.os) { +pub const HeapAllocator = switch (builtin.os.tag) { .windows => struct { allocator: Allocator, heap_handle: ?HeapHandle, @@ -855,7 +855,7 @@ test "PageAllocator" { try testAllocatorAlignedShrink(allocator); } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { // Trying really large alignment. As mentionned in the implementation, // VirtualAlloc returns 64K aligned addresses. We want to make sure // PageAllocator works beyond that, as it's not tested by @@ -868,7 +868,7 @@ test "PageAllocator" { } test "HeapAllocator" { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { var heap_allocator = HeapAllocator.init(); defer heap_allocator.deinit(); diff --git a/lib/std/io.zig b/lib/std/io.zig index 548f119b4f..6a2a080ef5 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -35,7 +35,7 @@ else pub const is_async = mode != .blocking; fn getStdOutHandle() os.fd_t { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return os.windows.peb().ProcessParameters.hStdOutput; } @@ -54,7 +54,7 @@ pub fn getStdOut() File { } fn getStdErrHandle() os.fd_t { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return os.windows.peb().ProcessParameters.hStdError; } @@ -74,7 +74,7 @@ pub fn getStdErr() File { } fn getStdInHandle() os.fd_t { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return os.windows.peb().ProcessParameters.hStdInput; } diff --git a/lib/std/mutex.zig b/lib/std/mutex.zig index 6954b2fb17..a57519cd14 100644 --- a/lib/std/mutex.zig +++ b/lib/std/mutex.zig @@ -73,7 +73,7 @@ pub const Mutex = if (builtin.single_threaded) return self.tryAcquire() orelse @panic("deadlock detected"); } } -else if (builtin.os == .windows) +else if (builtin.os.tag == .windows) // https://locklessinc.com/articles/keyed_events/ extern union { locked: u8, @@ -161,7 +161,7 @@ else if (builtin.os == .windows) } }; } -else if (builtin.link_libc or builtin.os == .linux) +else if (builtin.link_libc or builtin.os.tag == .linux) // stack-based version of https://github.com/Amanieu/parking_lot/blob/master/core/src/word_lock.rs struct { state: usize, diff --git a/lib/std/net.zig b/lib/std/net.zig index 898ba086be..b54803cd39 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -501,7 +501,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* return result; } - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { const flags = std.c.AI_NUMERICSERV; const family = os.AF_UNSPEC; var lookup_addrs = std.ArrayList(LookupAddr).init(allocator); diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 45d8b1cffd..4f3d955f30 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -63,7 +63,7 @@ test "parse and render IPv4 addresses" { } test "resolve DNS" { - if (std.builtin.os == .windows) { + if (std.builtin.os.tag == .windows) { // DNS resolution not implemented on Windows yet. return error.SkipZigTest; } @@ -81,7 +81,7 @@ test "resolve DNS" { test "listen on a port, send bytes, receive bytes" { if (!std.io.is_async) return error.SkipZigTest; - if (std.builtin.os != .linux) { + if (std.builtin.os.tag != .linux) { // TODO build abstractions for other operating systems return error.SkipZigTest; } diff --git a/lib/std/os.zig b/lib/std/os.zig index 3b60a08cef..6e96413d78 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -56,7 +56,7 @@ pub const system = if (@hasDecl(root, "os") and root.os != @This()) root.os.system else if (builtin.link_libc) std.c -else switch (builtin.os) { +else switch (builtin.os.tag) { .macosx, .ios, .watchos, .tvos => darwin, .freebsd => freebsd, .linux => linux, @@ -93,10 +93,10 @@ pub const errno = system.getErrno; /// must call `fsync` before `close`. /// Note: The Zig standard library does not support POSIX thread cancellation. pub fn close(fd: fd_t) void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.CloseHandle(fd); } - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { _ = wasi.fd_close(fd); } if (comptime std.Target.current.isDarwin()) { @@ -121,12 +121,12 @@ pub const GetRandomError = OpenError; /// appropriate OS-specific library call. Otherwise it uses the zig standard /// library implementation. pub fn getrandom(buffer: []u8) GetRandomError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.RtlGenRandom(buffer); } - if (builtin.os == .linux or builtin.os == .freebsd) { + if (builtin.os.tag == .linux or builtin.os.tag == .freebsd) { var buf = buffer; - const use_c = builtin.os != .linux or + const use_c = builtin.os.tag != .linux or std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok; while (buf.len != 0) { @@ -153,7 +153,7 @@ pub fn getrandom(buffer: []u8) GetRandomError!void { } return; } - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { switch (wasi.random_get(buffer.ptr, buffer.len)) { 0 => return, else => |err| return unexpectedErrno(err), @@ -188,13 +188,13 @@ pub fn abort() noreturn { // MSVCRT abort() sometimes opens a popup window which is undesirable, so // even when linking libc on Windows we use our own abort implementation. // See https://github.com/ziglang/zig/issues/2071 for more details. - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { if (builtin.mode == .Debug) { @breakpoint(); } windows.kernel32.ExitProcess(3); } - if (!builtin.link_libc and builtin.os == .linux) { + if (!builtin.link_libc and builtin.os.tag == .linux) { raise(SIGABRT) catch {}; // TODO the rest of the implementation of abort() from musl libc here @@ -202,10 +202,10 @@ pub fn abort() noreturn { raise(SIGKILL) catch {}; exit(127); } - if (builtin.os == .uefi) { + if (builtin.os.tag == .uefi) { exit(0); // TODO choose appropriate exit code } - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { @breakpoint(); exit(1); } @@ -223,7 +223,7 @@ pub fn raise(sig: u8) RaiseError!void { } } - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { var set: linux.sigset_t = undefined; // block application signals _ = linux.sigprocmask(SIG_BLOCK, &linux.app_mask, &set); @@ -260,16 +260,16 @@ pub fn exit(status: u8) noreturn { if (builtin.link_libc) { system.exit(status); } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { windows.kernel32.ExitProcess(status); } - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { wasi.proc_exit(status); } - if (builtin.os == .linux and !builtin.single_threaded) { + if (builtin.os.tag == .linux and !builtin.single_threaded) { linux.exit_group(status); } - if (builtin.os == .uefi) { + if (builtin.os.tag == .uefi) { // exit() is only avaliable if exitBootServices() has not been called yet. // This call to exit should not fail, so we don't care about its return value. if (uefi.system_table.boot_services) |bs| { @@ -299,11 +299,11 @@ pub const ReadError = error{ /// If the application has a global event loop enabled, EAGAIN is handled /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. pub fn read(fd: fd_t, buf: []u8) ReadError!usize { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.ReadFile(fd, buf, null); } - if (builtin.os == .wasi and !builtin.link_libc) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { const iovs = [1]iovec{iovec{ .iov_base = buf.ptr, .iov_len = buf.len, @@ -352,7 +352,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { /// * Windows /// On these systems, the read races with concurrent writes to the same file descriptor. pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { // TODO batch these into parallel requests var off: usize = 0; var iov_i: usize = 0; @@ -406,7 +406,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. pub fn pread(fd: fd_t, buf: []u8, offset: u64) ReadError!usize { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.ReadFile(fd, buf, offset); } @@ -493,7 +493,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize { } } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { // TODO batch these into parallel requests var off: usize = 0; var iov_i: usize = 0; @@ -557,11 +557,11 @@ pub const WriteError = error{ /// If the application has a global event loop enabled, EAGAIN is handled /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.WriteFile(fd, bytes, null); } - if (builtin.os == .wasi and !builtin.link_libc) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { const ciovs = [1]iovec_const{iovec_const{ .iov_base = bytes.ptr, .iov_len = bytes.len, @@ -1129,7 +1129,7 @@ pub fn getenv(key: []const u8) ?[]const u8 { } return null; } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { @compileError("std.os.getenv is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API."); } // TODO see https://github.com/ziglang/zig/issues/4524 @@ -1158,7 +1158,7 @@ pub fn getenvZ(key: [*:0]const u8) ?[]const u8 { const value = system.getenv(key) orelse return null; return mem.toSliceConst(u8, value); } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { @compileError("std.os.getenvZ is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API."); } return getenv(mem.toSliceConst(u8, key)); @@ -1167,7 +1167,7 @@ pub fn getenvZ(key: [*:0]const u8) ?[]const u8 { /// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name. /// See also `getenv`. pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 { - if (builtin.os != .windows) { + if (builtin.os.tag != .windows) { @compileError("std.os.getenvW is a Windows-only API"); } const key_slice = mem.toSliceConst(u16, key); @@ -1199,7 +1199,7 @@ pub const GetCwdError = error{ /// The result is a slice of out_buffer, indexed from 0. pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.GetCurrentDirectory(out_buffer); } @@ -1240,7 +1240,7 @@ pub const SymLinkError = error{ /// If `sym_link_path` exists, it will not be overwritten. /// See also `symlinkC` and `symlinkW`. pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const target_path_w = try windows.sliceToPrefixedFileW(target_path); const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path); return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0); @@ -1254,7 +1254,7 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError! /// This is the same as `symlink` except the parameters are null-terminated pointers. /// See also `symlink`. pub fn symlinkC(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const target_path_w = try windows.cStrToPrefixedFileW(target_path); const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path); return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0); @@ -1329,7 +1329,7 @@ pub const UnlinkError = error{ /// Delete a name and possibly the file it refers to. /// See also `unlinkC`. pub fn unlink(file_path: []const u8) UnlinkError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const file_path_w = try windows.sliceToPrefixedFileW(file_path); return windows.DeleteFileW(&file_path_w); } else { @@ -1340,7 +1340,7 @@ pub fn unlink(file_path: []const u8) UnlinkError!void { /// Same as `unlink` except the parameter is a null terminated UTF8-encoded string. pub fn unlinkC(file_path: [*:0]const u8) UnlinkError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const file_path_w = try windows.cStrToPrefixedFileW(file_path); return windows.DeleteFileW(&file_path_w); } @@ -1372,7 +1372,7 @@ pub const UnlinkatError = UnlinkError || error{ /// Asserts that the path parameter has no null bytes. pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { if (std.debug.runtime_safety) for (file_path) |byte| assert(byte != 0); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const file_path_w = try windows.sliceToPrefixedFileW(file_path); return unlinkatW(dirfd, &file_path_w, flags); } @@ -1382,7 +1382,7 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo /// Same as `unlinkat` but `file_path` is a null-terminated string. pub fn unlinkatC(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const file_path_w = try windows.cStrToPrefixedFileW(file_path_c); return unlinkatW(dirfd, &file_path_w, flags); } @@ -1493,7 +1493,7 @@ const RenameError = error{ /// Change the name or location of a file. pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const old_path_w = try windows.sliceToPrefixedFileW(old_path); const new_path_w = try windows.sliceToPrefixedFileW(new_path); return renameW(&old_path_w, &new_path_w); @@ -1506,7 +1506,7 @@ pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { /// Same as `rename` except the parameters are null-terminated byte arrays. pub fn renameC(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const old_path_w = try windows.cStrToPrefixedFileW(old_path); const new_path_w = try windows.cStrToPrefixedFileW(new_path); return renameW(&old_path_w, &new_path_w); @@ -1561,7 +1561,7 @@ pub const MakeDirError = error{ /// Create a directory. /// `mode` is ignored on Windows. pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); return windows.CreateDirectoryW(&dir_path_w, null); } else { @@ -1572,7 +1572,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { /// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string. pub fn mkdirC(dir_path: [*:0]const u8, mode: u32) MakeDirError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); return windows.CreateDirectoryW(&dir_path_w, null); } @@ -1611,7 +1611,7 @@ pub const DeleteDirError = error{ /// Deletes an empty directory. pub fn rmdir(dir_path: []const u8) DeleteDirError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); return windows.RemoveDirectoryW(&dir_path_w); } else { @@ -1622,7 +1622,7 @@ pub fn rmdir(dir_path: []const u8) DeleteDirError!void { /// Same as `rmdir` except the parameter is null-terminated. pub fn rmdirC(dir_path: [*:0]const u8) DeleteDirError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); return windows.RemoveDirectoryW(&dir_path_w); } @@ -1658,7 +1658,7 @@ pub const ChangeCurDirError = error{ /// Changes the current working directory of the calling process. /// `dir_path` is recommended to be a UTF-8 encoded string. pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); @compileError("TODO implement chdir for Windows"); } else { @@ -1669,7 +1669,7 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { /// Same as `chdir` except the parameter is null-terminated. pub fn chdirC(dir_path: [*:0]const u8) ChangeCurDirError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); @compileError("TODO implement chdir for Windows"); } @@ -1700,7 +1700,7 @@ pub const ReadLinkError = error{ /// Read value of a symbolic link. /// The return value is a slice of `out_buffer` from index 0. pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const file_path_w = try windows.sliceToPrefixedFileW(file_path); @compileError("TODO implement readlink for Windows"); } else { @@ -1711,7 +1711,7 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { /// Same as `readlink` except `file_path` is null-terminated. pub fn readlinkC(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const file_path_w = try windows.cStrToPrefixedFileW(file_path); @compileError("TODO implement readlink for Windows"); } @@ -1732,7 +1732,7 @@ pub fn readlinkC(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 } pub fn readlinkatC(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const file_path_w = try windows.cStrToPrefixedFileW(file_path); @compileError("TODO implement readlink for Windows"); } @@ -1800,7 +1800,7 @@ pub fn setregid(rgid: u32, egid: u32) SetIdError!void { /// Test whether a file descriptor refers to a terminal. pub fn isatty(handle: fd_t) bool { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { if (isCygwinPty(handle)) return true; @@ -1810,7 +1810,7 @@ pub fn isatty(handle: fd_t) bool { if (builtin.link_libc) { return system.isatty(handle) != 0; } - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { var statbuf: fdstat_t = undefined; const err = system.fd_fdstat_get(handle, &statbuf); if (err != 0) { @@ -1828,7 +1828,7 @@ pub fn isatty(handle: fd_t) bool { return true; } - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { var wsz: linux.winsize = undefined; return linux.syscall3(linux.SYS_ioctl, @bitCast(usize, @as(isize, handle)), linux.TIOCGWINSZ, @ptrToInt(&wsz)) == 0; } @@ -1836,7 +1836,7 @@ pub fn isatty(handle: fd_t) bool { } pub fn isCygwinPty(handle: fd_t) bool { - if (builtin.os != .windows) return false; + if (builtin.os.tag != .windows) return false; const size = @sizeOf(windows.FILE_NAME_INFO); var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (size + windows.MAX_PATH); @@ -2589,7 +2589,7 @@ pub const AccessError = error{ /// check user's permissions for a file /// TODO currently this assumes `mode` is `F_OK` on Windows. pub fn access(path: []const u8, mode: u32) AccessError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const path_w = try windows.sliceToPrefixedFileW(path); _ = try windows.GetFileAttributesW(&path_w); return; @@ -2603,7 +2603,7 @@ pub const accessC = accessZ; /// Same as `access` except `path` is null-terminated. pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const path_w = try windows.cStrToPrefixedFileW(path); _ = try windows.GetFileAttributesW(&path_w); return; @@ -2644,7 +2644,7 @@ pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!v /// Check user's permissions for a file, based on an open directory handle. /// TODO currently this ignores `mode` and `flags` on Windows. pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const path_w = try windows.sliceToPrefixedFileW(path); return faccessatW(dirfd, &path_w, mode, flags); } @@ -2654,7 +2654,7 @@ pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessErr /// Same as `faccessat` except the path parameter is null-terminated. pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const path_w = try windows.cStrToPrefixedFileW(path); return faccessatW(dirfd, &path_w, mode, flags); } @@ -2811,7 +2811,7 @@ pub const SeekError = error{Unseekable} || UnexpectedError; /// Repositions read/write file offset relative to the beginning. pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { - if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { + if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { var result: u64 = undefined; switch (errno(system.llseek(fd, offset, &result, SEEK_SET))) { 0 => return, @@ -2823,7 +2823,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { else => |err| return unexpectedErrno(err), } } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.SetFilePointerEx_BEGIN(fd, offset); } const ipos = @bitCast(i64, offset); // the OS treats this as unsigned @@ -2840,7 +2840,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { /// Repositions read/write file offset relative to the current offset. pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void { - if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { + if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { var result: u64 = undefined; switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_CUR))) { 0 => return, @@ -2852,7 +2852,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void { else => |err| return unexpectedErrno(err), } } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.SetFilePointerEx_CURRENT(fd, offset); } switch (errno(system.lseek(fd, offset, SEEK_CUR))) { @@ -2868,7 +2868,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void { /// Repositions read/write file offset relative to the end. pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void { - if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { + if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { var result: u64 = undefined; switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_END))) { 0 => return, @@ -2880,7 +2880,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void { else => |err| return unexpectedErrno(err), } } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.SetFilePointerEx_END(fd, offset); } switch (errno(system.lseek(fd, offset, SEEK_END))) { @@ -2896,7 +2896,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void { /// Returns the read/write file offset relative to the beginning. pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 { - if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { + if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { var result: u64 = undefined; switch (errno(system.llseek(fd, 0, &result, SEEK_CUR))) { 0 => return result, @@ -2908,7 +2908,7 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 { else => |err| return unexpectedErrno(err), } } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return windows.SetFilePointerEx_CURRENT_get(fd); } const rc = system.lseek(fd, 0, SEEK_CUR); @@ -2957,7 +2957,7 @@ pub const RealPathError = error{ /// The return value is a slice of `out_buffer`, but not necessarily from the beginning. /// See also `realpathC` and `realpathW`. pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const pathname_w = try windows.sliceToPrefixedFileW(pathname); return realpathW(&pathname_w, out_buffer); } @@ -2967,11 +2967,11 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE /// Same as `realpath` except `pathname` is null-terminated. pub fn realpathC(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const pathname_w = try windows.cStrToPrefixedFileW(pathname); return realpathW(&pathname_w, out_buffer); } - if (builtin.os == .linux and !builtin.link_libc) { + if (builtin.os.tag == .linux and !builtin.link_libc) { const fd = try openC(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0); defer close(fd); @@ -3121,7 +3121,7 @@ pub fn dl_iterate_phdr( pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError; pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void { - if (comptime std.Target.current.getOs() == .wasi) { + if (std.Target.current.os.tag == .wasi) { var ts: timestamp_t = undefined; switch (system.clock_time_get(@bitCast(u32, clk_id), 1, &ts)) { 0 => { @@ -3144,7 +3144,7 @@ pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void { } pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void { - if (comptime std.Target.current.getOs() == .wasi) { + if (std.Target.current.os.tag == .wasi) { var ts: timestamp_t = undefined; switch (system.clock_res_get(@bitCast(u32, clk_id), &ts)) { 0 => res.* = .{ @@ -3222,7 +3222,7 @@ pub const SigaltstackError = error{ } || UnexpectedError; pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void { - if (builtin.os == .windows or builtin.os == .uefi or builtin.os == .wasi) + if (builtin.os.tag == .windows or builtin.os.tag == .uefi or builtin.os.tag == .wasi) @compileError("std.os.sigaltstack not available for this target"); switch (errno(system.sigaltstack(ss, old_ss))) { @@ -3294,7 +3294,7 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 { else => |err| return unexpectedErrno(err), } } - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { var uts: utsname = undefined; switch (errno(system.uname(&uts))) { 0 => { @@ -3611,7 +3611,7 @@ pub const SchedYieldError = error{ }; pub fn sched_yield() SchedYieldError!void { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { // The return value has to do with how many other threads there are; it is not // an error condition on Windows. _ = windows.kernel32.SwitchToThread(); diff --git a/lib/std/os/bits.zig b/lib/std/os/bits.zig index bab9ad0ae5..38f019d775 100644 --- a/lib/std/os/bits.zig +++ b/lib/std/os/bits.zig @@ -3,10 +3,10 @@ //! Root source files can define `os.bits` and these will additionally be added //! to the namespace. -const builtin = @import("builtin"); +const std = @import("std"); const root = @import("root"); -pub usingnamespace switch (builtin.os) { +pub usingnamespace switch (std.Target.current.os.tag) { .macosx, .ios, .tvos, .watchos => @import("bits/darwin.zig"), .dragonfly => @import("bits/dragonfly.zig"), .freebsd => @import("bits/freebsd.zig"), diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index d11f206482..30dba85e51 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1070,7 +1070,7 @@ pub fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) usi } test "" { - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { _ = @import("linux/test.zig"); } } diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 488a557ed6..197edd82c1 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -53,7 +53,7 @@ test "std.Thread.getCurrentId" { thread.wait(); if (Thread.use_pthreads) { expect(thread_current_id == thread_id); - } else if (builtin.os == .windows) { + } else if (builtin.os.tag == .windows) { expect(Thread.getCurrentId() != thread_current_id); } else { // If the thread completes very quickly, then thread_id can be 0. See the @@ -151,7 +151,7 @@ test "realpath" { } test "sigaltstack" { - if (builtin.os == .windows or builtin.os == .wasi) return error.SkipZigTest; + if (builtin.os.tag == .windows or builtin.os.tag == .wasi) return error.SkipZigTest; var st: os.stack_t = undefined; try os.sigaltstack(null, &st); @@ -204,7 +204,7 @@ fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void { } test "dl_iterate_phdr" { - if (builtin.os == .windows or builtin.os == .wasi or builtin.os == .macosx) + if (builtin.os.tag == .windows or builtin.os.tag == .wasi or builtin.os.tag == .macosx) return error.SkipZigTest; var counter: usize = 0; @@ -213,7 +213,7 @@ test "dl_iterate_phdr" { } test "gethostname" { - if (builtin.os == .windows) + if (builtin.os.tag == .windows) return error.SkipZigTest; var buf: [os.HOST_NAME_MAX]u8 = undefined; @@ -222,7 +222,7 @@ test "gethostname" { } test "pipe" { - if (builtin.os == .windows) + if (builtin.os.tag == .windows) return error.SkipZigTest; var fds = try os.pipe(); @@ -241,7 +241,7 @@ test "argsAlloc" { test "memfd_create" { // memfd_create is linux specific. - if (builtin.os != .linux) return error.SkipZigTest; + if (builtin.os.tag != .linux) return error.SkipZigTest; const fd = std.os.memfd_create("test", 0) catch |err| switch (err) { // Related: https://github.com/ziglang/zig/issues/4019 error.SystemOutdated => return error.SkipZigTest, @@ -258,7 +258,7 @@ test "memfd_create" { } test "mmap" { - if (builtin.os == .windows) + if (builtin.os.tag == .windows) return error.SkipZigTest; // Simple mmap() call with non page-aligned size @@ -353,7 +353,7 @@ test "mmap" { } test "getenv" { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { expect(os.getenvW(&[_:0]u16{ 'B', 'O', 'G', 'U', 'S', 0x11, 0x22, 0x33, 0x44, 0x55 }) == null); } else { expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null); diff --git a/lib/std/packed_int_array.zig b/lib/std/packed_int_array.zig index 63b6fffa73..51be01315e 100644 --- a/lib/std/packed_int_array.zig +++ b/lib/std/packed_int_array.zig @@ -593,7 +593,7 @@ test "PackedInt(Array/Slice)Endian" { // after this one is not mapped and will cause a segfault if we // don't account for the bounds. test "PackedIntArray at end of available memory" { - switch (builtin.os) { + switch (builtin.os.tag) { .linux, .macosx, .ios, .freebsd, .netbsd, .windows => {}, else => return, } @@ -612,7 +612,7 @@ test "PackedIntArray at end of available memory" { } test "PackedIntSlice at end of available memory" { - switch (builtin.os) { + switch (builtin.os.tag) { .linux, .macosx, .ios, .freebsd, .netbsd, .windows => {}, else => return, } diff --git a/lib/std/process.zig b/lib/std/process.zig index 0dab8bb64b..118b5d9ab4 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -36,7 +36,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { var result = BufMap.init(allocator); errdefer result.deinit(); - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const ptr = os.windows.peb().ProcessParameters.Environment; var i: usize = 0; @@ -61,7 +61,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { try result.setMove(key, value); } return result; - } else if (builtin.os == .wasi) { + } else if (builtin.os.tag == .wasi) { var environ_count: usize = undefined; var environ_buf_size: usize = undefined; @@ -137,7 +137,7 @@ pub const GetEnvVarOwnedError = error{ /// Caller must free returned memory. pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { const result_w = blk: { const key_w = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); defer allocator.free(key_w); @@ -338,12 +338,12 @@ pub const ArgIteratorWindows = struct { }; pub const ArgIterator = struct { - const InnerType = if (builtin.os == .windows) ArgIteratorWindows else ArgIteratorPosix; + const InnerType = if (builtin.os.tag == .windows) ArgIteratorWindows else ArgIteratorPosix; inner: InnerType, pub fn init() ArgIterator { - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { // TODO: Figure out a compatible interface accomodating WASI @compileError("ArgIterator is not yet supported in WASI. Use argsAlloc and argsFree instead."); } @@ -355,7 +355,7 @@ pub const ArgIterator = struct { /// You must free the returned memory when done. pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) { - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { return self.inner.next(allocator); } else { return mem.dupe(allocator, u8, self.inner.next() orelse return null); @@ -380,7 +380,7 @@ pub fn args() ArgIterator { /// Caller must call argsFree on result. pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 { - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { var count: usize = undefined; var buf_size: usize = undefined; @@ -445,7 +445,7 @@ pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 { } pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { - if (builtin.os == .wasi) { + if (builtin.os.tag == .wasi) { const last_item = args_alloc[args_alloc.len - 1]; const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated const first_item_ptr = args_alloc[0].ptr; @@ -498,7 +498,7 @@ pub const UserInfo = struct { /// POSIX function which gets a uid from username. pub fn getUserInfo(name: []const u8) !UserInfo { - return switch (builtin.os) { + return switch (builtin.os.tag) { .linux, .macosx, .watchos, .tvos, .ios, .freebsd, .netbsd => posixGetUserInfo(name), else => @compileError("Unsupported OS"), }; @@ -591,7 +591,7 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { } pub fn getBaseAddress() usize { - switch (builtin.os) { + switch (builtin.os.tag) { .linux => { const base = os.system.getauxval(std.elf.AT_BASE); if (base != 0) { @@ -615,7 +615,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0] .Dynamic => {}, } const List = std.ArrayList([:0]u8); - switch (builtin.os) { + switch (builtin.os.tag) { .linux, .freebsd, .netbsd, diff --git a/lib/std/reset_event.zig b/lib/std/reset_event.zig index b31906c5f8..c28db809ca 100644 --- a/lib/std/reset_event.zig +++ b/lib/std/reset_event.zig @@ -16,7 +16,7 @@ pub const ResetEvent = struct { pub const OsEvent = if (builtin.single_threaded) DebugEvent - else if (builtin.link_libc and builtin.os != .windows and builtin.os != .linux) + else if (builtin.link_libc and builtin.os.tag != .windows and builtin.os.tag != .linux) PosixEvent else AtomicEvent; @@ -106,7 +106,7 @@ const PosixEvent = struct { fn deinit(self: *PosixEvent) void { // on dragonfly, *destroy() functions can return EINVAL // for statically initialized pthread structures - const err = if (builtin.os == .dragonfly) os.EINVAL else 0; + const err = if (builtin.os.tag == .dragonfly) os.EINVAL else 0; const retm = c.pthread_mutex_destroy(&self.mutex); assert(retm == 0 or retm == err); @@ -215,7 +215,7 @@ const AtomicEvent = struct { } } - pub const Futex = switch (builtin.os) { + pub const Futex = switch (builtin.os.tag) { .windows => WindowsFutex, .linux => LinuxFutex, else => SpinFutex, diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index 56ae3d0d8f..1f11fabca0 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -17,7 +17,7 @@ const is_msvc = switch (builtin.abi) { .msvc => true, else => false, }; -const is_freestanding = switch (builtin.os) { +const is_freestanding = switch (builtin.os.tag) { .freestanding => true, else => false, }; @@ -81,7 +81,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn @setCold(true); std.debug.panic("{}", .{msg}); } - if (builtin.os != .freestanding and builtin.os != .other) { + if (builtin.os.tag != .freestanding and builtin.os.tag != .other) { std.os.abort(); } while (true) {} @@ -178,11 +178,11 @@ test "test_bcmp" { comptime { if (builtin.mode != builtin.Mode.ReleaseFast and builtin.mode != builtin.Mode.ReleaseSmall and - builtin.os != builtin.Os.windows) + builtin.os.tag != .windows) { @export(__stack_chk_fail, .{ .name = "__stack_chk_fail" }); } - if (builtin.os == builtin.Os.linux) { + if (builtin.os.tag == .linux) { @export(clone, .{ .name = "clone" }); } } diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 8d49fdbd2a..3126e81b9d 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -1,11 +1,12 @@ -const builtin = @import("builtin"); +const std = @import("std"); +const builtin = std.builtin; const is_test = builtin.is_test; const is_gnu = switch (builtin.abi) { .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, else => false, }; -const is_mingw = builtin.os == .windows and is_gnu; +const is_mingw = builtin.os.tag == .windows and is_gnu; comptime { const linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak; @@ -180,7 +181,7 @@ comptime { @export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage }); @export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr8", .linkage = linkage }); - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { @export(@import("compiler_rt/arm.zig").__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage }); } @@ -250,7 +251,7 @@ comptime { @export(@import("compiler_rt/aullrem.zig")._aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { // Default stack-probe functions emitted by LLVM if (is_mingw) { @export(@import("compiler_rt/stack_probe.zig")._chkstk, .{ .name = "_alloca", .linkage = strong_linkage }); @@ -288,7 +289,7 @@ comptime { else => {}, } } else { - if (builtin.glibc_version != null) { + if (std.Target.current.isGnuLibC()) { @export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = linkage }); } @export(@import("compiler_rt/divti3.zig").__divti3, .{ .name = "__divti3", .linkage = linkage }); @@ -307,7 +308,7 @@ comptime { pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { @setCold(true); if (is_test) { - @import("std").debug.panic("{}", .{msg}); + std.debug.panic("{}", .{msg}); } else { unreachable; } diff --git a/lib/std/special/compiler_rt/extendXfYf2_test.zig b/lib/std/special/compiler_rt/extendXfYf2_test.zig index aa2faae901..e2664f6bae 100644 --- a/lib/std/special/compiler_rt/extendXfYf2_test.zig +++ b/lib/std/special/compiler_rt/extendXfYf2_test.zig @@ -90,7 +90,7 @@ test "extendhfsf2" { test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN // On x86 the NaN becomes quiet because the return is pushed on the x87 // stack due to ABI requirements - if (builtin.arch != .i386 and builtin.os == .windows) + if (builtin.arch != .i386 and builtin.os.tag == .windows) test__extendhfsf2(0x7c01, 0x7f802000); // sNaN test__extendhfsf2(0, 0); // 0 diff --git a/lib/std/spinlock.zig b/lib/std/spinlock.zig index 1a3239a95c..0af08e9a84 100644 --- a/lib/std/spinlock.zig +++ b/lib/std/spinlock.zig @@ -46,7 +46,7 @@ pub const SpinLock = struct { // and yielding for 380-410 iterations was found to be // a nice sweet spot. Posix systems on the other hand, // especially linux, perform better by yielding the thread. - switch (builtin.os) { + switch (builtin.os.tag) { .windows => loopHint(400), else => std.os.sched_yield() catch loopHint(1), } diff --git a/lib/std/start.zig b/lib/std/start.zig index b58b6e8144..b8e3e97f94 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -12,7 +12,7 @@ const start_sym_name = if (builtin.arch.isMIPS()) "__start" else "_start"; comptime { if (builtin.output_mode == .Lib and builtin.link_mode == .Dynamic) { - if (builtin.os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) { + if (builtin.os.tag == .windows and !@hasDecl(root, "_DllMainCRTStartup")) { @export(_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" }); } } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { @@ -20,17 +20,17 @@ comptime { if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { @export(main, .{ .name = "main", .linkage = .Weak }); } - } else if (builtin.os == .windows) { + } else if (builtin.os.tag == .windows) { if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) { @export(WinMainCRTStartup, .{ .name = "WinMainCRTStartup" }); } - } else if (builtin.os == .uefi) { + } else if (builtin.os.tag == .uefi) { if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" }); - } else if (builtin.arch.isWasm() and builtin.os == .freestanding) { + } else if (builtin.arch.isWasm() and builtin.os.tag == .freestanding) { if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name }); - } else if (builtin.os != .other and builtin.os != .freestanding) { + } else if (builtin.os.tag != .other and builtin.os.tag != .freestanding) { if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name }); } } @@ -78,7 +78,7 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv } fn _start() callconv(.Naked) noreturn { - if (builtin.os == builtin.Os.wasi) { + if (builtin.os.tag == .wasi) { // This is marked inline because for some reason LLVM in release mode fails to inline it, // and we want fewer call frames in stack traces. std.os.wasi.proc_exit(@call(.{ .modifier = .always_inline }, callMain, .{})); @@ -133,7 +133,7 @@ fn WinMainCRTStartup() callconv(.Stdcall) noreturn { // TODO https://github.com/ziglang/zig/issues/265 fn posixCallMainAndExit() noreturn { - if (builtin.os == builtin.Os.freebsd) { + if (builtin.os.tag == .freebsd) { @setAlignStack(16); } const argc = starting_stack_ptr[0]; @@ -144,7 +144,7 @@ fn posixCallMainAndExit() noreturn { while (envp_optional[envp_count]) |_| : (envp_count += 1) {} const envp = @ptrCast([*][*:0]u8, envp_optional)[0..envp_count]; - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { // Find the beginning of the auxiliary vector const auxv = @ptrCast([*]std.elf.Auxv, @alignCast(@alignOf(usize), envp.ptr + envp_count + 1)); std.os.linux.elf_aux_maybe = auxv; diff --git a/lib/std/target.zig b/lib/std/target.zig index 84056eae02..c9f8a247fe 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -11,143 +11,48 @@ pub const Target = struct { os: Os, abi: Abi, - /// The version ranges here represent the minimum OS version to be supported - /// and the maximum OS version to be supported. The default values represent - /// the range that the Zig Standard Library bases its abstractions on. - /// - /// The minimum version of the range is the main setting to tweak for a target. - /// Usually, the maximum target OS version will remain the default, which is - /// the latest released version of the OS. - /// - /// To test at compile time if the target is guaranteed to support a given OS feature, - /// one should check that the minimum version of the range is greater than or equal to - /// the version the feature was introduced in. - /// - /// To test at compile time if the target certainly will not support a given OS feature, - /// one should check that the maximum version of the range is less than the version the - /// feature was introduced in. - /// - /// If neither of these cases apply, a runtime check should be used to determine if the - /// target supports a given OS feature. - /// - /// Binaries built with a given maximum version will continue to function on newer operating system - /// versions. However, such a binary may not take full advantage of the newer operating system APIs. - pub const Os = union(enum) { - freestanding, - ananas, - cloudabi, - dragonfly, - freebsd: Version.Range, - fuchsia, - ios, - kfreebsd, - linux: LinuxVersionRange, - lv2, - macosx: Version.Range, - netbsd: Version.Range, - openbsd: Version.Range, - solaris, - windows: WindowsVersion.Range, - haiku, - minix, - rtems, - nacl, - cnk, - aix, - cuda, - nvcl, - amdhsa, - ps4, - elfiamcu, - tvos, - watchos, - mesa3d, - contiki, - amdpal, - hermit, - hurd, - wasi, - emscripten, - uefi, - other, + pub const Os = struct { + tag: Tag, + version_range: VersionRange, - /// See the documentation for `Os` for an explanation of the default version range. - pub fn defaultVersionRange(tag: @TagType(Os)) Os { - switch (tag) { - .freestanding => return .freestanding, - .ananas => return .ananas, - .cloudabi => return .cloudabi, - .dragonfly => return .dragonfly, - .freebsd => return .{ - .freebsd = Version.Range{ - .min = .{ .major = 12, .minor = 0 }, - .max = .{ .major = 12, .minor = 1 }, - }, - }, - .fuchsia => return .fuchsia, - .ios => return .ios, - .kfreebsd => return .kfreebsd, - .linux => return .{ - .linux = .{ - .range = .{ - .min = .{ .major = 3, .minor = 16 }, - .max = .{ .major = 5, .minor = 5, .patch = 5 }, - }, - .glibc = .{ .major = 2, .minor = 17 }, - }, - }, - .lv2 => return .lv2, - .macosx => return .{ - .min = .{ .major = 10, .minor = 13 }, - .max = .{ .major = 10, .minor = 15, .patch = 3 }, - }, - .netbsd => return .{ - .min = .{ .major = 8, .minor = 0 }, - .max = .{ .major = 9, .minor = 0 }, - }, - .openbsd => return .{ - .min = .{ .major = 6, .minor = 6 }, - .max = .{ .major = 6, .minor = 6 }, - }, - solaris => return .solaris, - windows => return .{ - .windows = .{ - .min = .win8_1, - .max = .win10_19h1, - }, - }, - haiku => return .haiku, - minix => return .minix, - rtems => return .rtems, - nacl => return .nacl, - cnk => return .cnk, - aix => return .aix, - cuda => return .cuda, - nvcl => return .nvcl, - amdhsa => return .amdhsa, - ps4 => return .ps4, - elfiamcu => return .elfiamcu, - tvos => return .tvos, - watchos => return .watchos, - mesa3d => return .mesa3d, - contiki => return .contiki, - amdpal => return .amdpal, - hermit => return .hermit, - hurd => return .hurd, - wasi => return .wasi, - emscripten => return .emscripten, - uefi => return .uefi, - other => return .other, - } - } - - pub const LinuxVersionRange = struct { - range: Version.Range, - glibc: Version, - - pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool { - return self.range.includesVersion(ver); - } + pub const Tag = enum { + freestanding, + ananas, + cloudabi, + dragonfly, + freebsd, + fuchsia, + ios, + kfreebsd, + linux, + lv2, + macosx, + netbsd, + openbsd, + solaris, + windows, + haiku, + minix, + rtems, + nacl, + cnk, + aix, + cuda, + nvcl, + amdhsa, + ps4, + elfiamcu, + tvos, + watchos, + mesa3d, + contiki, + amdpal, + hermit, + hurd, + wasi, + emscripten, + uefi, + other, }; /// Based on NTDDI version constants from @@ -178,29 +83,137 @@ pub const Target = struct { return @enumToInt(ver) >= @enumToInt(self.min) and @enumToInt(ver) <= @enumToInt(self.max); } }; + }; - pub fn nameToTag(name: []const u8) ?WindowsVersion { - const info = @typeInfo(WindowsVersion); - inline for (info.Enum.fields) |field| { - if (mem.eql(u8, name, field.name)) { - return @field(WindowsVersion, field.name); - } + pub const LinuxVersionRange = struct { + range: Version.Range, + glibc: Version, + + pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool { + return self.range.includesVersion(ver); + } + }; + + /// The version ranges here represent the minimum OS version to be supported + /// and the maximum OS version to be supported. The default values represent + /// the range that the Zig Standard Library bases its abstractions on. + /// + /// The minimum version of the range is the main setting to tweak for a target. + /// Usually, the maximum target OS version will remain the default, which is + /// the latest released version of the OS. + /// + /// To test at compile time if the target is guaranteed to support a given OS feature, + /// one should check that the minimum version of the range is greater than or equal to + /// the version the feature was introduced in. + /// + /// To test at compile time if the target certainly will not support a given OS feature, + /// one should check that the maximum version of the range is less than the version the + /// feature was introduced in. + /// + /// If neither of these cases apply, a runtime check should be used to determine if the + /// target supports a given OS feature. + /// + /// Binaries built with a given maximum version will continue to function on newer operating system + /// versions. However, such a binary may not take full advantage of the newer operating system APIs. + pub const VersionRange = union { + none: void, + semver: Version.Range, + linux: LinuxVersionRange, + windows: WindowsVersion.Range, + + /// The default `VersionRange` represents the range that the Zig Standard Library + /// bases its abstractions on. + pub fn default(tag: Tag) VersionRange { + switch (tag) { + .freestanding, + .ananas, + .cloudabi, + .dragonfly, + .fuchsia, + .ios, + .kfreebsd, + .lv2, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .tvos, + .watchos, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + .wasi, + .emscripten, + .uefi, + .other, + => return .{ .none = {} }, + + .freebsd => return .{ + .semver = Version.Range{ + .min = .{ .major = 12, .minor = 0 }, + .max = .{ .major = 12, .minor = 1 }, + }, + }, + .macosx => return .{ + .semver = .{ + .min = .{ .major = 10, .minor = 13 }, + .max = .{ .major = 10, .minor = 15, .patch = 3 }, + }, + }, + .netbsd => return .{ + .semver = .{ + .min = .{ .major = 8, .minor = 0 }, + .max = .{ .major = 9, .minor = 0 }, + }, + }, + .openbsd => return .{ + .semver = .{ + .min = .{ .major = 6, .minor = 6 }, + .max = .{ .major = 6, .minor = 6 }, + }, + }, + + .linux => return .{ + .linux = .{ + .range = .{ + .min = .{ .major = 3, .minor = 16 }, + .max = .{ .major = 5, .minor = 5, .patch = 5 }, + }, + .glibc = .{ .major = 2, .minor = 17 }, + }, + }, + + .windows => return .{ + .windows = .{ + .min = .win8_1, + .max = .win10_19h1, + }, + }, } - return null; } }; pub fn parse(text: []const u8) !Os { var it = mem.separate(text, "."); const os_name = it.next().?; - const tag = nameToTag(os_name) orelse return error.UnknownOperatingSystem; + const tag = std.meta.stringToEnum(Tag, os_name) orelse return error.UnknownOperatingSystem; const version_text = it.rest(); const S = struct { fn parseNone(s: []const u8) !void { if (s.len != 0) return error.InvalidOperatingSystemVersion; } - fn parseSemVer(s: []const u8, default: Version.Range) !Version.Range { - if (s.len == 0) return default; + fn parseSemVer(s: []const u8, d_range: Version.Range) !Version.Range { + if (s.len == 0) return d_range; var range_it = mem.separate(s, "..."); const min_text = range_it.next().?; @@ -212,7 +225,7 @@ pub const Target = struct { const max_text = range_it.next() orelse return Version.Range{ .min = min_ver, - .max = default.max, + .max = d_range.max, }; const max_ver = Version.parse(max_text) catch |err| switch (err) { error.Overflow => return error.InvalidOperatingSystemVersion, @@ -222,79 +235,93 @@ pub const Target = struct { return Version.Range{ .min = min_ver, .max = max_ver }; } - fn parseWindows(s: []const u8, default: WindowsVersion.Range) !WindowsVersion.Range { - if (s.len == 0) return default; + fn parseWindows(s: []const u8, d_range: WindowsVersion.Range) !WindowsVersion.Range { + if (s.len == 0) return d_range; var range_it = mem.separate(s, "..."); const min_text = range_it.next().?; - const min_ver = WindowsVersion.nameToTag(min_text) orelse + const min_ver = std.meta.stringToEnum(WindowsVersion, min_text) orelse return error.InvalidOperatingSystemVersion; const max_text = range_it.next() orelse return WindowsVersion.Range{ .min = min_ver, - .max = default.max, + .max = d_range.max, }; - const max_ver = WindowsVersion.nameToTag(max_text) orelse + const max_ver = std.meta.stringToEnum(WindowsVersion, max_text) orelse return error.InvalidOperatingSystemVersion; return WindowsVersion.Range{ .min = min_ver, .max = max_ver }; } }; - const default = defaultVersionRange(tag); + const d_range = VersionRange.default(tag); switch (tag) { - .freestanding => return Os{ .freestanding = try S.parseNone(version_text) }, - .ananas => return Os{ .ananas = try S.parseNone(version_text) }, - .cloudabi => return Os{ .cloudabi = try S.parseNone(version_text) }, - .dragonfly => return Os{ .dragonfly = try S.parseNone(version_text) }, - .freebsd => return Os{ .freebsd = try S.parseSemVer(version_text, default.freebsd) }, - .fuchsia => return Os{ .fuchsia = try S.parseNone(version_text) }, - .ios => return Os{ .ios = try S.parseNone(version_text) }, - .kfreebsd => return Os{ .kfreebsd = try S.parseNone(version_text) }, + .freestanding, + .ananas, + .cloudabi, + .dragonfly, + .fuchsia, + .ios, + .kfreebsd, + .lv2, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .tvos, + .watchos, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + .wasi, + .emscripten, + .uefi, + .other, + => return Os{ + .tag = tag, + .version_range = .{ .none = try S.parseNone(version_text) }, + }, + + .freebsd, + .macosx, + .netbsd, + .openbsd, + => return Os{ + .tag = tag, + .version_range = .{ .semver = try S.parseSemVer(version_text, d_range.semver) }, + }, + .linux => return Os{ - .linux = .{ - .range = try S.parseSemVer(version_text, default.linux.range), - .glibc = default.linux.glibc, + .tag = tag, + .version_range = .{ + .linux = .{ + .range = try S.parseSemVer(version_text, d_range.linux.range), + .glibc = d_range.linux.glibc, + }, }, }, - .lv2 => return Os{ .lv2 = try S.parseNone(version_text) }, - .macosx => return Os{ .macosx = try S.parseSemVer(version_text, default.macosx) }, - .netbsd => return Os{ .netbsd = try S.parseSemVer(version_text, default.netbsd) }, - .openbsd => return Os{ .openbsd = try S.parseSemVer(version_text, default.openbsd) }, - .solaris => return Os{ .solaris = try S.parseNone(version_text) }, - .windows => return Os{ .windows = try S.parseWindows(version_text, default.windows) }, - .haiku => return Os{ .haiku = try S.parseNone(version_text) }, - .minix => return Os{ .minix = try S.parseNone(version_text) }, - .rtems => return Os{ .rtems = try S.parseNone(version_text) }, - .nacl => return Os{ .nacl = try S.parseNone(version_text) }, - .cnk => return Os{ .cnk = try S.parseNone(version_text) }, - .aix => return Os{ .aix = try S.parseNone(version_text) }, - .cuda => return Os{ .cuda = try S.parseNone(version_text) }, - .nvcl => return Os{ .nvcl = try S.parseNone(version_text) }, - .amdhsa => return Os{ .amdhsa = try S.parseNone(version_text) }, - .ps4 => return Os{ .ps4 = try S.parseNone(version_text) }, - .elfiamcu => return Os{ .elfiamcu = try S.parseNone(version_text) }, - .tvos => return Os{ .tvos = try S.parseNone(version_text) }, - .watchos => return Os{ .watchos = try S.parseNone(version_text) }, - .mesa3d => return Os{ .mesa3d = try S.parseNone(version_text) }, - .contiki => return Os{ .contiki = try S.parseNone(version_text) }, - .amdpal => return Os{ .amdpal = try S.parseNone(version_text) }, - .hermit => return Os{ .hermit = try S.parseNone(version_text) }, - .hurd => return Os{ .hurd = try S.parseNone(version_text) }, - .wasi => return Os{ .wasi = try S.parseNone(version_text) }, - .emscripten => return Os{ .emscripten = try S.parseNone(version_text) }, - .uefi => return Os{ .uefi = try S.parseNone(version_text) }, - .other => return Os{ .other = try S.parseNone(version_text) }, + + .windows => return Os{ + .tag = tag, + .version_range = .{ .windows = try S.parseWindows(version_text, d_range.windows) }, + }, } } - pub fn nameToTag(name: []const u8) ?@TagType(Os) { - const info = @typeInfo(Os); - inline for (info.Union.fields) |field| { - if (mem.eql(u8, name, field.name)) { - return @field(Os, field.name); - } - } - return null; + pub fn defaultVersionRange(tag: Tag) Os { + return .{ + .tag = tag, + .version_range = VersionRange.default(tag), + }; } }; @@ -339,11 +366,10 @@ pub const Target = struct { macabi, pub fn default(arch: Cpu.Arch, target_os: Os) Abi { - switch (arch) { - .wasm32, .wasm64 => return .musl, - else => {}, + if (arch.isWasm()) { + return .musl; } - switch (target_os) { + switch (target_os.tag) { .freestanding, .ananas, .cloudabi, @@ -388,40 +414,19 @@ pub const Target = struct { } } - pub fn nameToTag(text: []const u8) ?Abi { - const info = @typeInfo(Abi); - inline for (info.Enum.fields) |field| { - if (mem.eql(u8, text, field.name)) { - return @field(Abi, field.name); - } - } - return null; - } - - pub fn parse(text: []const u8, os: *Os) !Abi { - var it = mem.separate(text, "."); - const tag = nameToTag(it.next().?) orelse return error.UnknownApplicationBinaryInterface; - const version_text = it.rest(); - if (version_text.len != 0) { - if (@as(@TagType(Os), os.*) == .linux and tag.isGnu()) { - os.linux.glibc = Version.parse(version_text) catch |err| switch (err) { - error.Overflow => return error.InvalidGlibcVersion, - error.InvalidCharacter => return error.InvalidGlibcVersion, - error.InvalidVersion => return error.InvalidGlibcVersion, - }; - } else { - return error.InvalidAbiVersion; - } - } - return tag; - } - pub fn isGnu(abi: Abi) bool { return switch (abi) { .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, else => false, }; } + + pub fn isMusl(abi: Abi) bool { + return switch (abi) { + .musl, .musleabi, .musleabihf => true, + else => false, + }; + } }; pub const ObjectFormat = enum { @@ -909,15 +914,15 @@ pub const Target = struct { /// TODO add OS version ranges and glibc version pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.getArch()), - @tagName(self.os), + @tagName(self.cpu.arch), + @tagName(self.os.tag), @tagName(self.abi), }); } /// Returned slice must be freed by the caller. pub fn vcpkgTriplet(allocator: *mem.Allocator, target: Target, linkage: std.build.VcpkgLinkage) ![]const u8 { - const arch = switch (target.getArch()) { + const arch = switch (target.cpu.arch) { .i386 => "x86", .x86_64 => "x64", @@ -957,16 +962,16 @@ pub const Target = struct { pub fn zigTripleNoSubArch(self: Target, allocator: *mem.Allocator) ![]u8 { return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.getArch()), - @tagName(self.os), + @tagName(self.cpu.arch), + @tagName(self.os.tag), @tagName(self.abi), }); } pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.getArch()), - @tagName(self.os), + @tagName(self.cpu.arch), + @tagName(self.os.tag), @tagName(self.abi), }); } @@ -1017,11 +1022,28 @@ pub const Target = struct { diags.arch = arch; const os_name = it.next() orelse return error.MissingOperatingSystem; - var os = try Os.parse(os_name); // var because Abi.parse can update linux.glibc version + var os = try Os.parse(os_name); diags.os = os; - const abi_name = it.next(); - const abi = if (abi_name) |n| try Abi.parse(n, &os) else Abi.default(arch, os); + const opt_abi_text = it.next(); + const abi = if (opt_abi_text) |abi_text| blk: { + var abi_it = mem.separate(abi_text, "."); + const abi = std.meta.stringToEnum(Abi, abi_it.next().?) orelse + return error.UnknownApplicationBinaryInterface; + const abi_ver_text = abi_it.rest(); + if (abi_ver_text.len != 0) { + if (os.tag == .linux and abi.isGnu()) { + os.version_range.linux.glibc = Version.parse(abi_ver_text) catch |err| switch (err) { + error.Overflow => return error.InvalidAbiVersion, + error.InvalidCharacter => return error.InvalidAbiVersion, + error.InvalidVersion => return error.InvalidAbiVersion, + }; + } else { + return error.InvalidAbiVersion; + } + } + break :blk abi; + } else Abi.default(arch, os); diags.abi = abi; if (it.next() != null) return error.UnexpectedExtraField; @@ -1130,25 +1152,6 @@ pub const Target = struct { } } - /// Deprecated; access the `os` field directly. - pub fn getOs(self: Target) @TagType(Os) { - return self.os; - } - - /// Deprecated; access the `cpu` field directly. - pub fn getCpu(self: Target) Cpu { - return self.cpu; - } - - /// Deprecated; access the `abi` field directly. - pub fn getAbi(self: Target) Abi { - return self.abi; - } - - pub fn getArch(self: Target) Cpu.Arch { - return self.cpu.arch; - } - pub fn getObjectFormat(self: Target) ObjectFormat { if (self.isWindows() or self.isUefi()) { return .coff; @@ -1170,28 +1173,25 @@ pub const Target = struct { } pub fn isMusl(self: Target) bool { - return switch (self.abi) { - .musl, .musleabi, .musleabihf => true, - else => false, - }; + return self.abi.isMusl(); } pub fn isDarwin(self: Target) bool { - return switch (self.os) { + return switch (self.os.tag) { .ios, .macosx, .watchos, .tvos => true, else => false, }; } pub fn isWindows(self: Target) bool { - return switch (self.os) { + return switch (self.os.tag) { .windows => true, else => false, }; } pub fn isLinux(self: Target) bool { - return switch (self.os) { + return switch (self.os.tag) { .linux => true, else => false, }; @@ -1205,40 +1205,41 @@ pub const Target = struct { } pub fn isDragonFlyBSD(self: Target) bool { - return switch (self.os) { + return switch (self.os.tag) { .dragonfly => true, else => false, }; } pub fn isUefi(self: Target) bool { - return switch (self.os) { + return switch (self.os.tag) { .uefi => true, else => false, }; } pub fn isWasm(self: Target) bool { - return switch (self.getArch()) { - .wasm32, .wasm64 => true, - else => false, - }; + return self.cpu.arch.isWasm(); } pub fn isFreeBSD(self: Target) bool { - return switch (self.os) { + return switch (self.os.tag) { .freebsd => true, else => false, }; } pub fn isNetBSD(self: Target) bool { - return switch (self.os) { + return switch (self.os.tag) { .netbsd => true, else => false, }; } + pub fn isGnuLibC(self: Target) bool { + return self.os.tag == .linux and self.abi.isGnu(); + } + pub fn wantSharedLibSymLinks(self: Target) bool { return !self.isWindows(); } @@ -1248,7 +1249,7 @@ pub const Target = struct { } pub fn getArchPtrBitWidth(self: Target) u32 { - switch (self.getArch()) { + switch (self.cpu.arch) { .avr, .msp430, => return 16, @@ -1323,8 +1324,8 @@ pub const Target = struct { if (@as(@TagType(Target), self) == .Native) return .native; // If the target OS matches the host OS, we can use QEMU to emulate a foreign architecture. - if (self.os == builtin.os) { - return switch (self.getArch()) { + if (self.os.tag == builtin.os.tag) { + return switch (self.cpu.arch) { .aarch64 => Executor{ .qemu = "qemu-aarch64" }, .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" }, .arm => Executor{ .qemu = "qemu-arm" }, @@ -1381,13 +1382,10 @@ pub const Target = struct { } pub fn hasDynamicLinker(self: Target) bool { - switch (self.getArch()) { - .wasm32, - .wasm64, - => return false, - else => {}, + if (self.cpu.arch.isWasm()) { + return false; } - switch (self.os) { + switch (self.os.tag) { .freestanding, .ios, .tvos, @@ -1424,7 +1422,7 @@ pub const Target = struct { defer result.deinit(); var is_arm = false; - switch (self.getArch()) { + switch (self.cpu.arch) { .arm, .thumb => { try result.append("arm"); is_arm = true; @@ -1442,11 +1440,11 @@ pub const Target = struct { return result.toOwnedSlice(); } - switch (self.os) { + switch (self.os.tag) { .freebsd => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.1"), .netbsd => return mem.dupeZ(a, u8, "/libexec/ld.elf_so"), .dragonfly => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.2"), - .linux => switch (self.getArch()) { + .linux => switch (self.cpu.arch) { .i386, .sparc, .sparcel, @@ -1539,7 +1537,7 @@ test "Target.parse" { .cpu_features = "x86_64-sse-sse2-avx-cx8", }); - std.testing.expect(target.os == .linux); + std.testing.expect(target.os.tag == .linux); std.testing.expect(target.abi == .gnu); std.testing.expect(target.cpu.arch == .x86_64); std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse)); @@ -1554,10 +1552,29 @@ test "Target.parse" { .cpu_features = "generic+v8a", }); - std.testing.expect(target.os == .linux); + std.testing.expect(target.os.tag == .linux); std.testing.expect(target.abi == .musleabihf); std.testing.expect(target.cpu.arch == .arm); std.testing.expect(target.cpu.model == &Target.arm.cpu.generic); std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a)); } + { + const target = try Target.parse(.{ + .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27", + .cpu_features = "generic+v8a", + }); + + std.testing.expect(target.cpu.arch == .aarch64); + std.testing.expect(target.os.tag == .linux); + std.testing.expect(target.os.version_range.linux.min.major == 3); + std.testing.expect(target.os.version_range.linux.min.minor == 10); + std.testing.expect(target.os.version_range.linux.min.patch == 0); + std.testing.expect(target.os.version_range.linux.max.major == 4); + std.testing.expect(target.os.version_range.linux.max.minor == 4); + std.testing.expect(target.os.version_range.linux.max.patch == 1); + std.testing.expect(target.os.version_range.linux.glibc.major == 2); + std.testing.expect(target.os.version_range.linux.glibc.minor == 27); + std.testing.expect(target.os.version_range.linux.glibc.patch == 0); + std.testing.expect(target.abi == .gnu); + } } diff --git a/lib/std/thread.zig b/lib/std/thread.zig index fcc71ae5a5..55db9d1733 100644 --- a/lib/std/thread.zig +++ b/lib/std/thread.zig @@ -9,14 +9,14 @@ const assert = std.debug.assert; pub const Thread = struct { data: Data, - pub const use_pthreads = builtin.os != .windows and builtin.link_libc; + pub const use_pthreads = builtin.os.tag != .windows and builtin.link_libc; /// Represents a kernel thread handle. /// May be an integer or a pointer depending on the platform. /// On Linux and POSIX, this is the same as Id. pub const Handle = if (use_pthreads) c.pthread_t - else switch (builtin.os) { + else switch (builtin.os.tag) { .linux => i32, .windows => windows.HANDLE, else => void, @@ -25,7 +25,7 @@ pub const Thread = struct { /// Represents a unique ID per thread. /// May be an integer or pointer depending on the platform. /// On Linux and POSIX, this is the same as Handle. - pub const Id = switch (builtin.os) { + pub const Id = switch (builtin.os.tag) { .windows => windows.DWORD, else => Handle, }; @@ -35,7 +35,7 @@ pub const Thread = struct { handle: Thread.Handle, memory: []align(mem.page_size) u8, } - else switch (builtin.os) { + else switch (builtin.os.tag) { .linux => struct { handle: Thread.Handle, memory: []align(mem.page_size) u8, @@ -55,7 +55,7 @@ pub const Thread = struct { if (use_pthreads) { return c.pthread_self(); } else - return switch (builtin.os) { + return switch (builtin.os.tag) { .linux => os.linux.gettid(), .windows => windows.kernel32.GetCurrentThreadId(), else => @compileError("Unsupported OS"), @@ -83,7 +83,7 @@ pub const Thread = struct { else => unreachable, } os.munmap(self.data.memory); - } else switch (builtin.os) { + } else switch (builtin.os.tag) { .linux => { while (true) { const pid_value = @atomicLoad(i32, &self.data.handle, .SeqCst); @@ -150,7 +150,7 @@ pub const Thread = struct { const Context = @TypeOf(context); comptime assert(@typeInfo(@TypeOf(startFn)).Fn.args[0].arg_type.? == Context); - if (builtin.os == builtin.Os.windows) { + if (builtin.os.tag == .windows) { const WinThread = struct { const OuterContext = struct { thread: Thread, @@ -309,7 +309,7 @@ pub const Thread = struct { os.EINVAL => unreachable, else => return os.unexpectedErrno(@intCast(usize, err)), } - } else if (builtin.os == .linux) { + } else if (builtin.os.tag == .linux) { var flags: u32 = os.CLONE_VM | os.CLONE_FS | os.CLONE_FILES | os.CLONE_SIGHAND | os.CLONE_THREAD | os.CLONE_SYSVSEM | os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID | os.CLONE_DETACHED; @@ -369,11 +369,11 @@ pub const Thread = struct { }; pub fn cpuCount() CpuCountError!usize { - if (builtin.os == .linux) { + if (builtin.os.tag == .linux) { const cpu_set = try os.sched_getaffinity(0); return @as(usize, os.CPU_COUNT(cpu_set)); // TODO should not need this usize cast } - if (builtin.os == .windows) { + if (builtin.os.tag == .windows) { var system_info: windows.SYSTEM_INFO = undefined; windows.kernel32.GetSystemInfo(&system_info); return @intCast(usize, system_info.dwNumberOfProcessors); diff --git a/lib/std/time.zig b/lib/std/time.zig index 63d3ecce18..4112fb7bda 100644 --- a/lib/std/time.zig +++ b/lib/std/time.zig @@ -1,5 +1,5 @@ -const builtin = @import("builtin"); const std = @import("std.zig"); +const builtin = std.builtin; const assert = std.debug.assert; const testing = std.testing; const os = std.os; @@ -7,10 +7,12 @@ const math = std.math; pub const epoch = @import("time/epoch.zig"); +const is_windows = std.Target.current.os.tag == .windows; + /// Spurious wakeups are possible and no precision of timing is guaranteed. /// TODO integrate with evented I/O pub fn sleep(nanoseconds: u64) void { - if (builtin.os == .windows) { + if (is_windows) { const ns_per_ms = ns_per_s / ms_per_s; const big_ms_from_ns = nanoseconds / ns_per_ms; const ms = math.cast(os.windows.DWORD, big_ms_from_ns) catch math.maxInt(os.windows.DWORD); @@ -31,7 +33,7 @@ pub fn timestamp() u64 { /// Get the posix timestamp, UTC, in milliseconds /// TODO audit this function. is it possible to return an error? pub fn milliTimestamp() u64 { - if (builtin.os == .windows) { + if (is_windows) { //FileTime has a granularity of 100 nanoseconds // and uses the NTFS/Windows epoch var ft: os.windows.FILETIME = undefined; @@ -42,7 +44,7 @@ pub fn milliTimestamp() u64 { const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime; return @divFloor(ft64, hns_per_ms) - -epoch_adj; } - if (builtin.os == .wasi and !builtin.link_libc) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { var ns: os.wasi.timestamp_t = undefined; // TODO: Verify that precision is ignored @@ -102,7 +104,7 @@ pub const Timer = struct { ///if we used resolution's value when performing the /// performance counter calc on windows/darwin, it would /// be less precise - frequency: switch (builtin.os) { + frequency: switch (builtin.os.tag) { .windows => u64, .macosx, .ios, .tvos, .watchos => os.darwin.mach_timebase_info_data, else => void, @@ -127,7 +129,7 @@ pub const Timer = struct { pub fn start() Error!Timer { var self: Timer = undefined; - if (builtin.os == .windows) { + if (is_windows) { self.frequency = os.windows.QueryPerformanceFrequency(); self.resolution = @divFloor(ns_per_s, self.frequency); self.start_time = os.windows.QueryPerformanceCounter(); @@ -172,7 +174,7 @@ pub const Timer = struct { } fn clockNative() u64 { - if (builtin.os == .windows) { + if (is_windows) { return os.windows.QueryPerformanceCounter(); } if (comptime std.Target.current.isDarwin()) { @@ -184,7 +186,7 @@ pub const Timer = struct { } fn nativeDurationToNanos(self: Timer, duration: u64) u64 { - if (builtin.os == .windows) { + if (is_windows) { return @divFloor(duration * ns_per_s, self.frequency); } if (comptime std.Target.current.isDarwin()) { diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 3931e362c6..5c9b71001b 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -1,11 +1,14 @@ const std = @import("../std.zig"); +const elf = std.elf; const mem = std.mem; +const fs = std.fs; const Allocator = std.mem.Allocator; const ArrayList = std.ArrayList; const assert = std.debug.assert; const process = std.process; +const Target = std.Target; -const is_windows = std.Target.current.isWindows(); +const is_windows = Target.current.os.tag == .windows; pub const NativePaths = struct { include_dirs: ArrayList([:0]u8), @@ -77,7 +80,7 @@ pub const NativePaths = struct { } if (!is_windows) { - const triple = try std.Target.current.linuxTriple(allocator); + const triple = try Target.current.linuxTriple(allocator); // TODO: $ ld --verbose | grep SEARCH_DIR // the output contains some paths that end with lib64, maybe include them too? @@ -161,3 +164,326 @@ pub const NativePaths = struct { try array.append(item); } }; + +pub const NativeTargetInfo = struct { + target: Target, + dynamic_linker: ?[:0]u8, + + pub const DetectError = error{ + OutOfMemory, + FileSystem, + SystemResources, + SymLinkLoop, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + DeviceBusy, + }; + + /// Detects the native CPU model & features, operating system & version, and C ABI & dynamic linker. + /// On Linux, this is additionally responsible for detecting the native glibc version when applicable. + pub fn detect(allocator: *Allocator) DetectError!NativeTargetInfo { + const arch = Target.current.cpu.arch; + const os_tag = Target.current.os.tag; + + // TODO Detect native CPU model & features. Until that is implemented we hard code baseline. + const cpu = Target.Cpu.baseline(arch); + + // TODO Detect native operating system version. Until that is implemented we use the minimum version + // of the default range. + const os = Target.Os.defaultVersionRange(os_tag); + + return detectAbiAndDynamicLinker(allocator, cpu, os); + } + + /// Must be the same `Allocator` passed to `detect`. + pub fn deinit(self: *NativeTargetInfo, allocator: *Allocator) void { + if (self.dynamic_linker) |dl| allocator.free(dl); + self.* = undefined; + } + + /// First we attempt to use the executable's own binary. If it is dynamically + /// linked, then it should answer both the C ABI question and the dynamic linker question. + /// If it is statically linked, then we try /usr/bin/env. If that does not provide the answer, then + /// we fall back to the defaults. + fn detectAbiAndDynamicLinker( + allocator: *Allocator, + cpu: Target.Cpu, + os: Target.Os, + ) DetectError!NativeTargetInfo { + if (!comptime Target.current.hasDynamicLinker()) { + return defaultAbiAndDynamicLinker(allocator, cpu, os); + } + // The current target's ABI cannot be relied on for this. For example, we may build the zig + // compiler for target riscv64-linux-musl and provide a tarball for users to download. + // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined + // and supported by Zig. But that means that we must detect the system ABI here rather than + // relying on `Target.current`. + const LdInfo = struct { + ld_path: []u8, + abi: Target.Abi, + }; + var ld_info_list = std.ArrayList(LdInfo).init(allocator); + defer { + for (ld_info_list.toSlice()) |ld_info| allocator.free(ld_info.ld_path); + ld_info_list.deinit(); + } + + const all_abis = comptime blk: { + assert(@enumToInt(Target.Abi.none) == 0); + const fields = std.meta.fields(Target.Abi)[1..]; + var array: [fields.len]Target.Abi = undefined; + inline for (fields) |field, i| { + array[i] = @field(Target.Abi, field.name); + } + break :blk array; + }; + for (all_abis) |abi| { + // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and + // skip adding it to `ld_info_list`. + const target: Target = .{ + .cpu = cpu, + .os = os, + .abi = abi, + }; + const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue, + }; + errdefer allocator.free(standard_ld_path); + try ld_info_list.append(.{ + .ld_path = standard_ld_path, + .abi = abi, + }); + } + + // Best case scenario: the executable is dynamically linked, and we can iterate + // over our own shared objects and find a dynamic linker. + self_exe: { + const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator); + defer allocator.free(lib_paths); + + var found_ld_info: LdInfo = undefined; + var found_ld_path: [:0]const u8 = undefined; + + // Look for dynamic linker. + // This is O(N^M) but typical case here is N=2 and M=10. + find_ld: for (lib_paths) |lib_path| { + for (ld_info_list.toSlice()) |ld_info| { + const standard_ld_basename = fs.path.basename(ld_info.ld_path); + if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) { + found_ld_info = ld_info; + found_ld_path = lib_path; + break :find_ld; + } + } + } else break :self_exe; + + // Look for glibc version. + var os_adjusted = os; + if (Target.current.os.tag == .linux and found_ld_info.abi.isGnu()) { + for (lib_paths) |lib_path| { + if (std.mem.endsWith(u8, lib_path, glibc_so_basename)) { + os_adjusted.version_range.linux.glibc = glibcVerFromSO(lib_path) catch |err| switch (err) { + error.UnrecognizedGnuLibCFileName => continue, + error.InvalidGnuLibCVersion => continue, + error.GnuLibCVersionUnavailable => continue, + else => |e| return e, + }; + break; + } + } + } + + return NativeTargetInfo{ + .target = .{ + .cpu = cpu, + .os = os_adjusted, + .abi = found_ld_info.abi, + }, + .dynamic_linker = try mem.dupeZ(allocator, u8, found_ld_path), + }; + } + + // If Zig is statically linked, such as via distributed binary static builds, the above + // trick won't work. The next thing we fall back to is the same thing, but for /usr/bin/env. + // Since that path is hard-coded into the shebang line of many portable scripts, it's a + // reasonably reliable path to check for. + return abiAndDynamicLinkerFromUsrBinEnv(allocator, cpu, os) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.FileSystem => return error.FileSystem, + error.SystemResources => return error.SystemResources, + error.SymLinkLoop => return error.SymLinkLoop, + error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded => return error.SystemFdQuotaExceeded, + error.DeviceBusy => return error.DeviceBusy, + + error.UnableToReadElfFile, + error.ElfNotADynamicExecutable, + error.InvalidElfProgramHeaders, + error.InvalidElfClass, + error.InvalidElfVersion, + error.InvalidElfEndian, + error.InvalidElfFile, + error.InvalidElfMagic, + error.UsrBinEnvNotAvailable, + error.Unexpected, + // Finally, we fall back on the standard path. + => defaultAbiAndDynamicLinker(allocator, cpu, os), + }; + } + + const glibc_so_basename = "libc.so.6"; + + fn glibcVerFromSO(so_path: [:0]const u8) !std.builtin.Version { + var link_buf: [std.os.PATH_MAX]u8 = undefined; + const link_name = std.os.readlinkC(so_path.ptr, &link_buf) catch |err| switch (err) { + error.AccessDenied => return error.GnuLibCVersionUnavailable, + error.FileSystem => return error.FileSystem, + error.SymLinkLoop => return error.SymLinkLoop, + error.NameTooLong => unreachable, + error.FileNotFound => return error.GnuLibCVersionUnavailable, + error.SystemResources => return error.SystemResources, + error.NotDir => return error.GnuLibCVersionUnavailable, + error.Unexpected => return error.GnuLibCVersionUnavailable, + }; + // example: "libc-2.3.4.so" + // example: "libc-2.27.so" + const prefix = "libc-"; + const suffix = ".so"; + if (!mem.startsWith(u8, link_name, prefix) or !mem.endsWith(u8, link_name, suffix)) { + return error.UnrecognizedGnuLibCFileName; + } + // chop off "libc-" and ".so" + const link_name_chopped = link_name[prefix.len .. link_name.len - suffix.len]; + return std.builtin.Version.parse(link_name_chopped) catch |err| switch (err) { + error.Overflow => return error.InvalidGnuLibCVersion, + error.InvalidCharacter => return error.InvalidGnuLibCVersion, + error.InvalidVersion => return error.InvalidGnuLibCVersion, + }; + } + + fn abiAndDynamicLinkerFromUsrBinEnv( + allocator: *Allocator, + cpu: Target.Cpu, + os: Target.Os, + ) !NativeTargetInfo { + const env_file = std.fs.openFileAbsoluteC("/usr/bin/env", .{}) catch |err| switch (err) { + error.NoSpaceLeft => unreachable, + error.NameTooLong => unreachable, + error.PathAlreadyExists => unreachable, + error.SharingViolation => unreachable, + error.InvalidUtf8 => unreachable, + error.BadPathName => unreachable, + error.PipeBusy => unreachable, + + error.IsDir => return error.UsrBinEnvNotAvailable, + error.NotDir => return error.UsrBinEnvNotAvailable, + error.AccessDenied => return error.UsrBinEnvNotAvailable, + error.NoDevice => return error.UsrBinEnvNotAvailable, + error.FileNotFound => return error.UsrBinEnvNotAvailable, + error.FileTooBig => return error.UsrBinEnvNotAvailable, + + else => |e| return e, + }; + var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined; + const hdr_bytes_len = try wrapRead(env_file.pread(&hdr_buf, 0)); + if (hdr_bytes_len < @sizeOf(elf.Elf32_Ehdr)) return error.InvalidElfFile; + const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf); + const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf); + if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; + const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) { + elf.ELFDATA2LSB => .Little, + elf.ELFDATA2MSB => .Big, + else => return error.InvalidElfEndian, + }; + const need_bswap = elf_endian != std.builtin.endian; + if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion; + + const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) { + elf.ELFCLASS32 => false, + elf.ELFCLASS64 => true, + else => return error.InvalidElfClass, + }; + var phoff = elfInt(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff); + const phentsize = elfInt(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize); + const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum); + const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); + + const ph_total_size = std.math.mul(u32, phentsize, phnum) catch |err| switch (err) { + error.Overflow => return error.InvalidElfProgramHeaders, + }; + var ph_buf: [16 * @sizeOf(elf.Elf64_Phdr)]u8 align(@alignOf(elf.Elf64_Phdr)) = undefined; + var ph_i: u16 = 0; + while (ph_i < phnum) { + // Reserve some bytes so that we can deref the 64-bit struct fields even when the ELF file is 32-bits. + const reserve = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr); + const read_byte_len = try wrapRead(env_file.pread(ph_buf[0 .. ph_buf.len - reserve], phoff)); + if (read_byte_len < phentsize) return error.ElfNotADynamicExecutable; + var buf_i: usize = 0; + while (buf_i < read_byte_len and ph_i < phnum) : ({ + ph_i += 1; + phoff += phentsize; + buf_i += phentsize; + }) { + const ph32 = @ptrCast(*elf.Elf32_Phdr, @alignCast(@alignOf(elf.Elf32_Phdr), &ph_buf[buf_i])); + const ph64 = @ptrCast(*elf.Elf64_Phdr, @alignCast(@alignOf(elf.Elf64_Phdr), &ph_buf[buf_i])); + const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type); + switch (p_type) { + elf.PT_INTERP => { + std.debug.warn("found PT_INTERP\n", .{}); + }, + elf.PT_DYNAMIC => { + std.debug.warn("found PT_DYNAMIC\n", .{}); + }, + else => continue, + } + } + } + + return error.OutOfMemory; // TODO + } + + fn wrapRead(res: std.os.ReadError!usize) !usize { + return res catch |err| switch (err) { + error.OperationAborted => unreachable, // Windows-only + error.WouldBlock => unreachable, // Did not request blocking mode + error.SystemResources => return error.SystemResources, + error.IsDir => return error.UnableToReadElfFile, + error.BrokenPipe => return error.UnableToReadElfFile, + error.ConnectionResetByPeer => return error.UnableToReadElfFile, + error.Unexpected => return error.Unexpected, + error.InputOutput => return error.FileSystem, + }; + } + + fn defaultAbiAndDynamicLinker(allocator: *Allocator, cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { + const target: Target = .{ + .cpu = cpu, + .os = os, + .abi = Target.Abi.default(cpu.arch, os), + }; + return @as(NativeTargetInfo, .{ + .target = target, + .dynamic_linker = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => null, + }, + }); + } +}; + +fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) { + if (is_64) { + if (need_bswap) { + return @byteSwap(@TypeOf(int_64), int_64); + } else { + return int_64; + } + } else { + if (need_bswap) { + return @byteSwap(@TypeOf(int_32), int_32); + } else { + return int_32; + } + } +} diff --git a/src-self-hosted/c_int.zig b/src-self-hosted/c_int.zig index 2a840372b9..1ee27c7596 100644 --- a/src-self-hosted/c_int.zig +++ b/src-self-hosted/c_int.zig @@ -70,7 +70,7 @@ pub const CInt = struct { pub fn sizeInBits(cint: CInt, self: Target) u32 { const arch = self.getArch(); - switch (self.getOs()) { + switch (self.os.tag) { .freestanding, .other => switch (self.getArch()) { .msp430 => switch (cint.id) { .Short, diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index d2db306be8..bdaa8b86dc 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -1050,7 +1050,7 @@ pub const struct_ZigClangExprEvalResult = extern struct { pub const struct_ZigClangAPValue = extern struct { Kind: ZigClangAPValueKind, - Data: if (builtin.os == .windows and builtin.abi == .msvc) [52]u8 else [68]u8, + Data: if (builtin.os.tag == .windows and builtin.abi == .msvc) [52]u8 else [68]u8, }; pub extern fn ZigClangVarDecl_getTypeSourceInfo_getType(self: *const struct_ZigClangVarDecl) struct_ZigClangQualType; diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig index 11838e7e63..c7f5690cc3 100644 --- a/src-self-hosted/introspect.zig +++ b/src-self-hosted/introspect.zig @@ -1,4 +1,4 @@ -// Introspection and determination of system libraries needed by zig. +//! Introspection and determination of system libraries needed by zig. const std = @import("std"); const mem = std.mem; @@ -6,14 +6,6 @@ const fs = std.fs; const warn = std.debug.warn; -pub fn detectDynamicLinker(allocator: *mem.Allocator, target: std.Target) ![:0]u8 { - if (target == .Native) { - return @import("libc_installation.zig").detectNativeDynamicLinker(allocator); - } else { - return target.getStandardDynamicLinkerPath(allocator); - } -} - /// Caller must free result pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 { const test_zig_dir = try fs.path.join(allocator, &[_][]const u8{ test_path, "lib", "zig" }); diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 85740240ab..d617abb821 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -99,27 +99,27 @@ pub const LibCInstallation = struct { return error.ParseError; } if (self.crt_dir == null and !is_darwin) { - try stderr.print("crt_dir may not be empty for {}\n", .{@tagName(Target.current.getOs())}); + try stderr.print("crt_dir may not be empty for {}\n", .{@tagName(Target.current.os.tag)}); return error.ParseError; } if (self.static_crt_dir == null and is_windows and is_gnu) { try stderr.print("static_crt_dir may not be empty for {}-{}\n", .{ - @tagName(Target.current.getOs()), - @tagName(Target.current.getAbi()), + @tagName(Target.current.os.tag), + @tagName(Target.current.abi), }); return error.ParseError; } if (self.msvc_lib_dir == null and is_windows and !is_gnu) { try stderr.print("msvc_lib_dir may not be empty for {}-{}\n", .{ - @tagName(Target.current.getOs()), - @tagName(Target.current.getAbi()), + @tagName(Target.current.os.tag), + @tagName(Target.current.abi), }); return error.ParseError; } if (self.kernel32_lib_dir == null and is_windows and !is_gnu) { try stderr.print("kernel32_lib_dir may not be empty for {}-{}\n", .{ - @tagName(Target.current.getOs()), - @tagName(Target.current.getAbi()), + @tagName(Target.current.os.tag), + @tagName(Target.current.abi), }); return error.ParseError; } @@ -616,104 +616,6 @@ fn printVerboseInvocation( } } -/// Caller owns returned memory. -pub fn detectNativeDynamicLinker(allocator: *Allocator) error{ - OutOfMemory, - TargetHasNoDynamicLinker, - UnknownDynamicLinkerPath, -}![:0]u8 { - if (!comptime Target.current.hasDynamicLinker()) { - return error.TargetHasNoDynamicLinker; - } - - // The current target's ABI cannot be relied on for this. For example, we may build the zig - // compiler for target riscv64-linux-musl and provide a tarball for users to download. - // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined - // and supported by Zig. But that means that we must detect the system ABI here rather than - // relying on `std.Target.current`. - - const LdInfo = struct { - ld_path: []u8, - abi: Target.Abi, - }; - var ld_info_list = std.ArrayList(LdInfo).init(allocator); - defer { - for (ld_info_list.toSlice()) |ld_info| allocator.free(ld_info.ld_path); - ld_info_list.deinit(); - } - - const all_abis = comptime blk: { - const fields = std.meta.fields(Target.Abi); - var array: [fields.len]Target.Abi = undefined; - inline for (fields) |field, i| { - array[i] = @field(Target.Abi, field.name); - } - break :blk array; - }; - for (all_abis) |abi| { - // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and - // skip adding it to `ld_info_list`. - const target: Target = .{ - .Cross = .{ - .cpu = Target.Cpu.baseline(Target.current.getArch()), - .os = Target.current.getOs(), - .abi = abi, - }, - }; - const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue, - }; - errdefer allocator.free(standard_ld_path); - try ld_info_list.append(.{ - .ld_path = standard_ld_path, - .abi = abi, - }); - } - - // Best case scenario: the zig compiler is dynamically linked, and we can iterate - // over our own shared objects and find a dynamic linker. - { - const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator); - defer allocator.free(lib_paths); - - // This is O(N^M) but typical case here is N=2 and M=10. - for (lib_paths) |lib_path| { - for (ld_info_list.toSlice()) |ld_info| { - const standard_ld_basename = fs.path.basename(ld_info.ld_path); - if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) { - return std.mem.dupeZ(allocator, u8, lib_path); - } - } - } - } - - // If Zig is statically linked, such as via distributed binary static builds, the above - // trick won't work. What are we left with? Try to run the system C compiler and get - // it to tell us the dynamic linker path. - // TODO: instead of this, look at the shared libs of /usr/bin/env. - for (ld_info_list.toSlice()) |ld_info| { - const standard_ld_basename = fs.path.basename(ld_info.ld_path); - - const full_ld_path = ccPrintFileName(.{ - .allocator = allocator, - .search_basename = standard_ld_basename, - .want_dirname = .full_path, - }) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.LibCRuntimeNotFound, - error.CCompilerExitCode, - error.CCompilerCrashed, - error.UnableToSpawnCCompiler, - => continue, - }; - return full_ld_path; - } - - // Finally, we fall back on the standard path. - return Target.current.getStandardDynamicLinkerPath(allocator); -} - const Search = struct { path: []const u8, version: []const u8, diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 0a29da4778..1efa15574a 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -515,7 +515,7 @@ const DarwinPlatform = struct { break :blk ver; }, .None => blk: { - assert(comp.target.getOs() == .macosx); + assert(comp.target.os.tag == .macosx); result.kind = .MacOS; break :blk "10.14"; }, @@ -534,7 +534,7 @@ const DarwinPlatform = struct { } if (result.kind == .IPhoneOS) { - switch (comp.target.getArch()) { + switch (comp.target.cpu.arch) { .i386, .x86_64, => result.kind = .IPhoneOSSimulator, diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index fbfd1e2642..264a896f46 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -79,9 +79,9 @@ pub fn main() !void { } else if (mem.eql(u8, cmd, "libc")) { return cmdLibC(allocator, cmd_args); } else if (mem.eql(u8, cmd, "targets")) { - // TODO figure out the current target rather than using the target that was specified when - // compiling the compiler - return @import("print_targets.zig").cmdTargets(allocator, cmd_args, stdout, Target.current); + const info = try std.zig.system.NativeTargetInfo.detect(allocator); + defer info.deinit(allocator); + return @import("print_targets.zig").cmdTargets(allocator, cmd_args, stdout, info.target); } else if (mem.eql(u8, cmd, "version")) { return cmdVersion(allocator, cmd_args); } else if (mem.eql(u8, cmd, "zen")) { diff --git a/src-self-hosted/print_targets.zig b/src-self-hosted/print_targets.zig index 16f1891164..be024a2a04 100644 --- a/src-self-hosted/print_targets.zig +++ b/src-self-hosted/print_targets.zig @@ -124,7 +124,7 @@ pub fn cmdTargets( try jws.objectField("os"); try jws.beginArray(); - inline for (@typeInfo(Target.Os).Enum.fields) |field| { + inline for (@typeInfo(Target.Os.Tag).Enum.fields) |field| { try jws.arrayElem(); try jws.emitString(field.name); } @@ -201,16 +201,16 @@ pub fn cmdTargets( try jws.objectField("cpu"); try jws.beginObject(); try jws.objectField("arch"); - try jws.emitString(@tagName(native_target.getArch())); + try jws.emitString(@tagName(native_target.cpu.arch)); try jws.objectField("name"); - const cpu = native_target.getCpu(); + const cpu = native_target.cpu; try jws.emitString(cpu.model.name); { try jws.objectField("features"); try jws.beginArray(); - for (native_target.getArch().allFeaturesList()) |feature, i_usize| { + for (native_target.cpu.arch.allFeaturesList()) |feature, i_usize| { const index = @intCast(Target.Cpu.Feature.Set.Index, i_usize); if (cpu.features.isEnabled(index)) { try jws.arrayElem(); @@ -222,9 +222,9 @@ pub fn cmdTargets( try jws.endObject(); } try jws.objectField("os"); - try jws.emitString(@tagName(native_target.getOs())); + try jws.emitString(@tagName(native_target.os.tag)); try jws.objectField("abi"); - try jws.emitString(@tagName(native_target.getAbi())); + try jws.emitString(@tagName(native_target.abi)); // TODO implement native glibc version detection in self-hosted try jws.endObject(); diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index dacdb86bcd..7f0f35abe0 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -110,6 +110,8 @@ const Error = extern enum { WindowsSdkNotFound, UnknownDynamicLinkerPath, TargetHasNoDynamicLinker, + InvalidAbiVersion, + InvalidOperatingSystemVersion, }; const FILE = std.c.FILE; @@ -633,11 +635,11 @@ export fn stage2_cmd_targets(zig_triple: [*:0]const u8) c_int { fn cmdTargets(zig_triple: [*:0]const u8) !void { var target = try Target.parse(.{ .arch_os_abi = mem.toSliceConst(u8, zig_triple) }); - target.Cross.cpu = blk: { + target.cpu = blk: { const llvm = @import("llvm.zig"); const llvm_cpu_name = llvm.GetHostCPUName(); const llvm_cpu_features = llvm.GetNativeFeatures(); - break :blk try detectNativeCpuWithLLVM(target.getArch(), llvm_cpu_name, llvm_cpu_features); + break :blk try detectNativeCpuWithLLVM(target.cpu.arch, llvm_cpu_name, llvm_cpu_features); }; return @import("print_targets.zig").cmdTargets( std.heap.c_allocator, @@ -662,6 +664,14 @@ export fn stage2_target_parse( error.MissingArchitecture => return .MissingArchitecture, error.InvalidLlvmCpuFeaturesFormat => return .InvalidLlvmCpuFeaturesFormat, error.UnexpectedExtraField => return .SemanticAnalyzeFail, + error.InvalidAbiVersion => return .InvalidAbiVersion, + error.InvalidOperatingSystemVersion => return .InvalidOperatingSystemVersion, + error.FileSystem => return .FileSystem, + error.SymLinkLoop => return .SymLinkLoop, + error.SystemResources => return .SystemResources, + error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded, + error.DeviceBusy => return .DeviceBusy, }; return .None; } @@ -671,108 +681,48 @@ fn stage2TargetParse( zig_triple_oz: ?[*:0]const u8, mcpu_oz: ?[*:0]const u8, ) !void { - const target: Target = if (zig_triple_oz) |zig_triple_z| blk: { + const target: std.build.Target = if (zig_triple_oz) |zig_triple_z| blk: { const zig_triple = mem.toSliceConst(u8, zig_triple_z); const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else "baseline"; var diags: std.Target.ParseOptions.Diagnostics = .{}; - break :blk Target.parse(.{ - .arch_os_abi = zig_triple, - .cpu_features = mcpu, - .diagnostics = &diags, - }) catch |err| switch (err) { - error.UnknownCpu => { - std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{ - diags.cpu_name.?, - @tagName(diags.arch.?), - }); - for (diags.arch.?.allCpuModels()) |cpu| { - std.debug.warn(" {}\n", .{cpu.name}); - } - process.exit(1); + break :blk std.build.Target{ + .Cross = Target.parse(.{ + .arch_os_abi = zig_triple, + .cpu_features = mcpu, + .diagnostics = &diags, + }) catch |err| switch (err) { + error.UnknownCpu => { + std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{ + diags.cpu_name.?, + @tagName(diags.arch.?), + }); + for (diags.arch.?.allCpuModels()) |cpu| { + std.debug.warn(" {}\n", .{cpu.name}); + } + process.exit(1); + }, + error.UnknownCpuFeature => { + std.debug.warn( + \\Unknown CPU feature: '{}' + \\Available CPU features for architecture '{}': + \\ + , .{ + diags.unknown_feature_name, + @tagName(diags.arch.?), + }); + for (diags.arch.?.allFeaturesList()) |feature| { + std.debug.warn(" {}: {}\n", .{ feature.name, feature.description }); + } + process.exit(1); + }, + else => |e| return e, }, - error.UnknownCpuFeature => { - std.debug.warn( - \\Unknown CPU feature: '{}' - \\Available CPU features for architecture '{}': - \\ - , .{ - diags.unknown_feature_name, - @tagName(diags.arch.?), - }); - for (diags.arch.?.allFeaturesList()) |feature| { - std.debug.warn(" {}: {}\n", .{ feature.name, feature.description }); - } - process.exit(1); - }, - else => |e| return e, }; - } else Target.Native; + } else std.build.Target.Native; try stage1_target.fromTarget(target); } -fn initStage1TargetCpuFeatures(stage1_target: *Stage2Target, cpu: Target.Cpu) !void { - const allocator = std.heap.c_allocator; - const cache_hash = try std.fmt.allocPrint0(allocator, "{}\n{}", .{ - cpu.model.name, - cpu.features.asBytes(), - }); - errdefer allocator.free(cache_hash); - - const generic_arch_name = cpu.arch.genericName(); - var builtin_str_buffer = try std.Buffer.allocPrint(allocator, - \\Cpu{{ - \\ .arch = .{}, - \\ .model = &Target.{}.cpu.{}, - \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{ - \\ - , .{ - @tagName(cpu.arch), - generic_arch_name, - cpu.model.name, - generic_arch_name, - generic_arch_name, - }); - defer builtin_str_buffer.deinit(); - - var llvm_features_buffer = try std.Buffer.initSize(allocator, 0); - defer llvm_features_buffer.deinit(); - - for (cpu.arch.allFeaturesList()) |feature, index_usize| { - const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize); - const is_enabled = cpu.features.isEnabled(index); - - if (feature.llvm_name) |llvm_name| { - const plus_or_minus = "-+"[@boolToInt(is_enabled)]; - try llvm_features_buffer.appendByte(plus_or_minus); - try llvm_features_buffer.append(llvm_name); - try llvm_features_buffer.append(","); - } - - if (is_enabled) { - // TODO some kind of "zig identifier escape" function rather than - // unconditionally using @"" syntax - try builtin_str_buffer.append(" .@\""); - try builtin_str_buffer.append(feature.name); - try builtin_str_buffer.append("\",\n"); - } - } - - try builtin_str_buffer.append( - \\ }), - \\}; - \\ - ); - - assert(mem.endsWith(u8, llvm_features_buffer.toSliceConst(), ",")); - llvm_features_buffer.shrink(llvm_features_buffer.len() - 1); - - stage1_target.llvm_cpu_name = if (cpu.model.llvm_name) |s| s.ptr else null; - stage1_target.llvm_cpu_features = llvm_features_buffer.toOwnedSlice().ptr; - stage1_target.builtin_str = builtin_str_buffer.toOwnedSlice().ptr; - stage1_target.cache_hash = cache_hash.ptr; -} - // ABI warning const Stage2LibCInstallation = extern struct { include_dir: [*:0]const u8, @@ -952,10 +902,13 @@ const Stage2Target = extern struct { llvm_cpu_name: ?[*:0]const u8, llvm_cpu_features: ?[*:0]const u8, - builtin_str: ?[*:0]const u8, + cpu_builtin_str: ?[*:0]const u8, cache_hash: ?[*:0]const u8, + os_builtin_str: ?[*:0]const u8, - fn toTarget(in_target: Stage2Target) Target { + dynamic_linker: ?[*:0]const u8, + + fn toTarget(in_target: Stage2Target) std.build.Target { if (in_target.is_native) return .Native; const in_arch = in_target.arch - 1; // skip over ZigLLVM_UnknownArch @@ -965,39 +918,244 @@ const Stage2Target = extern struct { return .{ .Cross = .{ .cpu = Target.Cpu.baseline(enumInt(Target.Cpu.Arch, in_arch)), - .os = enumInt(Target.Os, in_os), + .os = Target.Os.defaultVersionRange(enumInt(Target.Os.Tag, in_os)), .abi = enumInt(Target.Abi, in_abi), }, }; } - fn fromTarget(self: *Stage2Target, target: Target) !void { - const cpu = switch (target) { + fn fromTarget(self: *Stage2Target, build_target: std.build.Target) !void { + const allocator = std.heap.c_allocator; + var dynamic_linker: ?[*:0]u8 = null; + const target = switch (build_target) { .Native => blk: { - // TODO self-host CPU model and feature detection instead of relying on LLVM + const info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator); + if (info.dynamic_linker) |dl| { + dynamic_linker = dl.ptr; + } + + // TODO we want to just use info.target but implementing CPU model & feature detection is todo + // so here we rely on LLVM const llvm = @import("llvm.zig"); const llvm_cpu_name = llvm.GetHostCPUName(); const llvm_cpu_features = llvm.GetNativeFeatures(); - break :blk try detectNativeCpuWithLLVM(target.getArch(), llvm_cpu_name, llvm_cpu_features); + const arch = std.Target.current.cpu.arch; + var t = info.target; + t.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); + break :blk t; }, - .Cross => target.getCpu(), + .Cross => |t| t, }; + + var cache_hash = try std.Buffer.allocPrint(allocator, "{}\n{}\n", .{ + target.cpu.model.name, + target.cpu.features.asBytes(), + }); + defer cache_hash.deinit(); + + const generic_arch_name = target.cpu.arch.genericName(); + var cpu_builtin_str_buffer = try std.Buffer.allocPrint(allocator, + \\Cpu{{ + \\ .arch = .{}, + \\ .model = &Target.{}.cpu.{}, + \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{ + \\ + , .{ + @tagName(target.cpu.arch), + generic_arch_name, + target.cpu.model.name, + generic_arch_name, + generic_arch_name, + }); + defer cpu_builtin_str_buffer.deinit(); + + var llvm_features_buffer = try std.Buffer.initSize(allocator, 0); + defer llvm_features_buffer.deinit(); + + for (target.cpu.arch.allFeaturesList()) |feature, index_usize| { + const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize); + const is_enabled = target.cpu.features.isEnabled(index); + + if (feature.llvm_name) |llvm_name| { + const plus_or_minus = "-+"[@boolToInt(is_enabled)]; + try llvm_features_buffer.appendByte(plus_or_minus); + try llvm_features_buffer.append(llvm_name); + try llvm_features_buffer.append(","); + } + + if (is_enabled) { + // TODO some kind of "zig identifier escape" function rather than + // unconditionally using @"" syntax + try cpu_builtin_str_buffer.append(" .@\""); + try cpu_builtin_str_buffer.append(feature.name); + try cpu_builtin_str_buffer.append("\",\n"); + } + } + + try cpu_builtin_str_buffer.append( + \\ }), + \\}; + \\ + ); + + assert(mem.endsWith(u8, llvm_features_buffer.toSliceConst(), ",")); + llvm_features_buffer.shrink(llvm_features_buffer.len() - 1); + + var os_builtin_str_buffer = try std.Buffer.allocPrint(allocator, + \\Os{{ + \\ .tag = .{}, + \\ .version_range = .{{ + , .{@tagName(target.os.tag)}); + defer os_builtin_str_buffer.deinit(); + + // We'll re-use the OS version range builtin string for the cache hash. + const os_builtin_str_ver_start_index = os_builtin_str_buffer.len(); + + @setEvalBranchQuota(2000); + switch (target.os.tag) { + .freestanding, + .ananas, + .cloudabi, + .dragonfly, + .fuchsia, + .ios, + .kfreebsd, + .lv2, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .tvos, + .watchos, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + .wasi, + .emscripten, + .uefi, + .other, + => try os_builtin_str_buffer.append(" .none = {} }\n"), + + .freebsd, + .macosx, + .netbsd, + .openbsd, + => try os_builtin_str_buffer.print( + \\.semver = .{{ + \\ .min = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ .max = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ }}}}, + , .{ + target.os.version_range.semver.min.major, + target.os.version_range.semver.min.minor, + target.os.version_range.semver.min.patch, + + target.os.version_range.semver.max.major, + target.os.version_range.semver.max.minor, + target.os.version_range.semver.max.patch, + }), + + .linux => try os_builtin_str_buffer.print( + \\.linux = .{{ + \\ .range = .{{ + \\ .min = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ .max = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ }}, + \\ .glibc = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ }}}}, + \\ + , .{ + target.os.version_range.linux.range.min.major, + target.os.version_range.linux.range.min.minor, + target.os.version_range.linux.range.min.patch, + + target.os.version_range.linux.range.max.major, + target.os.version_range.linux.range.max.minor, + target.os.version_range.linux.range.max.patch, + + target.os.version_range.linux.glibc.major, + target.os.version_range.linux.glibc.minor, + target.os.version_range.linux.glibc.patch, + }), + + .windows => try os_builtin_str_buffer.print( + \\.semver = .{{ + \\ .min = .{}, + \\ .max = .{}, + \\ }}}}, + , .{ + @tagName(target.os.version_range.windows.min), + @tagName(target.os.version_range.windows.max), + }), + } + try os_builtin_str_buffer.append("};\n"); + + try cache_hash.append( + os_builtin_str_buffer.toSlice()[os_builtin_str_ver_start_index..os_builtin_str_buffer.len()], + ); + + const glibc_version = if (target.isGnuLibC()) blk: { + const stage1_glibc = try std.heap.c_allocator.create(Stage2GLibCVersion); + const stage2_glibc = target.os.version_range.linux.glibc; + stage1_glibc.* = .{ + .major = stage2_glibc.major, + .minor = stage2_glibc.minor, + .patch = stage2_glibc.patch, + }; + break :blk stage1_glibc; + } else null; + self.* = .{ - .arch = @enumToInt(target.getArch()) + 1, // skip over ZigLLVM_UnknownArch + .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch .vendor = 0, - .os = @enumToInt(target.getOs()), - .abi = @enumToInt(target.getAbi()), - .llvm_cpu_name = null, - .llvm_cpu_features = null, - .builtin_str = null, - .cache_hash = null, - .is_native = target == .Native, - .glibc_version = null, + .os = @enumToInt(target.os.tag), + .abi = @enumToInt(target.abi), + .llvm_cpu_name = if (target.cpu.model.llvm_name) |s| s.ptr else null, + .llvm_cpu_features = llvm_features_buffer.toOwnedSlice().ptr, + .cpu_builtin_str = cpu_builtin_str_buffer.toOwnedSlice().ptr, + .os_builtin_str = os_builtin_str_buffer.toOwnedSlice().ptr, + .cache_hash = cache_hash.toOwnedSlice().ptr, + .is_native = build_target == .Native, + .glibc_version = glibc_version, + .dynamic_linker = dynamic_linker, }; - try initStage1TargetCpuFeatures(self, cpu); } }; +fn enumInt(comptime Enum: type, int: c_int) Enum { + return @intToEnum(Enum, @intCast(@TagType(Enum), int)); +} + // ABI warning const Stage2GLibCVersion = extern struct { major: u32, @@ -1005,26 +1163,6 @@ const Stage2GLibCVersion = extern struct { patch: u32, }; -// ABI warning -export fn stage2_detect_dynamic_linker(in_target: *const Stage2Target, out_ptr: *[*:0]u8, out_len: *usize) Error { - const target = in_target.toTarget(); - const result = @import("introspect.zig").detectDynamicLinker( - std.heap.c_allocator, - target, - ) catch |err| switch (err) { - error.OutOfMemory => return .OutOfMemory, - error.UnknownDynamicLinkerPath => return .UnknownDynamicLinkerPath, - error.TargetHasNoDynamicLinker => return .TargetHasNoDynamicLinker, - }; - out_ptr.* = result.ptr; - out_len.* = result.len; - return .None; -} - -fn enumInt(comptime Enum: type, int: c_int) Enum { - return @intToEnum(Enum, @intCast(@TagType(Enum), int)); -} - // ABI warning const Stage2NativePaths = extern struct { include_dirs_ptr: [*][*:0]u8, diff --git a/src-self-hosted/util.zig b/src-self-hosted/util.zig index 04c5420d26..2a7bf4d9cc 100644 --- a/src-self-hosted/util.zig +++ b/src-self-hosted/util.zig @@ -34,25 +34,3 @@ pub fn initializeAllTargets() void { llvm.InitializeAllAsmPrinters(); llvm.InitializeAllAsmParsers(); } - -pub fn getTriple(allocator: *std.mem.Allocator, self: std.Target) !std.Buffer { - var result = try std.Buffer.initSize(allocator, 0); - errdefer result.deinit(); - - // LLVM WebAssembly output support requires the target to be activated at - // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly. - // - // LLVM determines the output format based on the abi suffix, - // defaulting to an object based on the architecture. The default format in - // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to - // explicitly set this ourself in order for it to work. - // - // This is fixed in LLVM 7 and you will be able to get wasm output by - // using the target triple `wasm32-unknown-unknown-unknown`. - const env_name = if (self.isWasm()) "wasm" else @tagName(self.getAbi()); - - var out = &std.io.BufferOutStream.init(&result).stream; - try out.print("{}-unknown-{}-{}", .{ @tagName(self.getArch()), @tagName(self.getOs()), env_name }); - - return result; -} diff --git a/src/codegen.cpp b/src/codegen.cpp index ee3e05a801..15122d5e11 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4483,7 +4483,7 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutableGen *execu if (!type_has_bits(field->type_entry)) { ZigType *tag_type = union_type->data.unionation.tag_type; - if (!instruction->initializing || !type_has_bits(tag_type)) + if (!instruction->initializing || tag_type == nullptr || !type_has_bits(tag_type)) return nullptr; // The field has no bits but we still have to change the discriminant @@ -8543,25 +8543,24 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { buf_appendf(contents, "pub const link_mode = LinkMode.%s;\n", link_type); buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build)); buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); - buf_appendf(contents, "pub const os = Os.%s;\n", cur_os); + buf_append_str(contents, "/// Deprecated: use `std.Target.cpu.arch`\n"); buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch); buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi); { buf_append_str(contents, "pub const cpu: Cpu = "); - if (g->zig_target->builtin_str != nullptr) { - buf_append_str(contents, g->zig_target->builtin_str); + if (g->zig_target->cpu_builtin_str != nullptr) { + buf_append_str(contents, g->zig_target->cpu_builtin_str); } else { - buf_append_str(contents, "Target.Cpu.baseline(arch);\n"); + buf_appendf(contents, "Target.Cpu.baseline(.%s);\n", cur_arch); } } - if (g->libc_link_lib != nullptr && g->zig_target->glibc_version != nullptr) { - buf_appendf(contents, - "pub const glibc_version: ?Version = Version{.major = %d, .minor = %d, .patch = %d};\n", - g->zig_target->glibc_version->major, - g->zig_target->glibc_version->minor, - g->zig_target->glibc_version->patch); - } else { - buf_appendf(contents, "pub const glibc_version: ?Version = null;\n"); + { + buf_append_str(contents, "pub const os = "); + if (g->zig_target->os_builtin_str != nullptr) { + buf_append_str(contents, g->zig_target->os_builtin_str); + } else { + buf_appendf(contents, "Target.Os.defaultVersionRange(.%s);\n", cur_os); + } } buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); @@ -8867,8 +8866,6 @@ static void init(CodeGen *g) { } static void detect_dynamic_linker(CodeGen *g) { - Error err; - if (g->dynamic_linker_path != nullptr) return; if (!g->have_dynamic_link) @@ -8876,16 +8873,9 @@ static void detect_dynamic_linker(CodeGen *g) { if (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic)) return; - char *dynamic_linker_ptr; - size_t dynamic_linker_len; - if ((err = stage2_detect_dynamic_linker(g->zig_target, &dynamic_linker_ptr, &dynamic_linker_len))) { - if (err == ErrorTargetHasNoDynamicLinker) return; - fprintf(stderr, "Unable to detect dynamic linker: %s\n", err_str(err)); - exit(1); + if (g->zig_target->dynamic_linker != nullptr) { + g->dynamic_linker_path = buf_create_from_str(g->zig_target->dynamic_linker); } - g->dynamic_linker_path = buf_create_from_mem(dynamic_linker_ptr, dynamic_linker_len); - // Skips heap::c_allocator because the memory is allocated by stage2 library. - free(dynamic_linker_ptr); } static void detect_libc(CodeGen *g) { diff --git a/src/error.cpp b/src/error.cpp index 730c6e7193..2e92a98217 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -81,6 +81,8 @@ const char *err_str(Error err) { case ErrorWindowsSdkNotFound: return "Windows SDK not found"; case ErrorUnknownDynamicLinkerPath: return "unknown dynamic linker path"; case ErrorTargetHasNoDynamicLinker: return "target has no dynamic linker"; + case ErrorInvalidAbiVersion: return "invalid C ABI version"; + case ErrorInvalidOperatingSystemVersion: return "invalid operating system version"; } return "(invalid error)"; } diff --git a/src/main.cpp b/src/main.cpp index a4ab719123..11612402ee 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -89,8 +89,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --single-threaded source may assume it is only used single-threaded\n" " -dynamic create a shared library (.so; .dll; .dylib)\n" " --strip exclude debug symbols\n" - " -target [name] -- see the targets command\n" - " -target-glibc [version] target a specific glibc version (default: 2.17)\n" + " -target [name] -- see the targets command\n" " --verbose-tokenize enable compiler debug output for tokenization\n" " --verbose-ast enable compiler debug output for AST parsing\n" " --verbose-link enable compiler debug output for linking\n" @@ -419,7 +418,6 @@ static int main0(int argc, char **argv) { const char *mios_version_min = nullptr; const char *linker_script = nullptr; Buf *version_script = nullptr; - const char *target_glibc = nullptr; ZigList rpath_list = {0}; bool each_lib_rpath = false; ZigList objects = {0}; @@ -853,8 +851,6 @@ static int main0(int argc, char **argv) { linker_script = argv[i]; } else if (strcmp(arg, "--version-script") == 0) { version_script = buf_create_from_str(argv[i]); - } else if (strcmp(arg, "-target-glibc") == 0) { - target_glibc = argv[i]; } else if (strcmp(arg, "-rpath") == 0) { rpath_list.append(argv[i]); } else if (strcmp(arg, "--test-filter") == 0) { @@ -982,29 +978,6 @@ static int main0(int argc, char **argv) { "See `%s targets` to display valid targets.\n", err_str(err), arg0); return print_error_usage(arg0); } - if (target_is_glibc(&target)) { - target.glibc_version = heap::c_allocator.create(); - - if (target_glibc != nullptr) { - if ((err = target_parse_glibc_version(target.glibc_version, target_glibc))) { - fprintf(stderr, "invalid glibc version '%s': %s\n", target_glibc, err_str(err)); - return print_error_usage(arg0); - } - } else { - target_init_default_glibc_version(&target); -#if defined(ZIG_OS_LINUX) - if (target.is_native) { - // TODO self-host glibc version detection, and then this logic can go away - if ((err = glibc_detect_native_version(target.glibc_version))) { - // Fall back to the default version. - } - } -#endif - } - } else if (target_glibc != nullptr) { - fprintf(stderr, "'%s' is not a glibc-compatible target", target_string); - return print_error_usage(arg0); - } Buf zig_triple_buf = BUF_INIT; target_triple_zig(&zig_triple_buf, &target); diff --git a/src/stage2.cpp b/src/stage2.cpp index 736f11622e..2023b45aaf 100644 --- a/src/stage2.cpp +++ b/src/stage2.cpp @@ -100,13 +100,11 @@ Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, cons if (mcpu == nullptr) { target->llvm_cpu_name = ZigLLVMGetHostCPUName(); target->llvm_cpu_features = ZigLLVMGetNativeFeatures(); - target->builtin_str = "Target.Cpu.baseline(arch);\n"; target->cache_hash = "native\n\n"; } else if (strcmp(mcpu, "baseline") == 0) { target->is_native = false; target->llvm_cpu_name = ""; target->llvm_cpu_features = ""; - target->builtin_str = "Target.Cpu.baseline(arch);\n"; target->cache_hash = "baseline\n\n"; } else { const char *msg = "stage0 can't handle CPU/features in the target"; @@ -148,7 +146,6 @@ Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, cons const char *msg = "stage0 can't handle CPU/features in the target"; stage2_panic(msg, strlen(msg)); } - target->builtin_str = "Target.Cpu.baseline(arch);\n"; target->cache_hash = "\n\n"; } @@ -186,11 +183,6 @@ enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc) { stage2_panic(msg, strlen(msg)); } -enum Error stage2_detect_dynamic_linker(const struct ZigTarget *target, char **out_ptr, size_t *out_len) { - const char *msg = "stage0 called stage2_detect_dynamic_linker"; - stage2_panic(msg, strlen(msg)); -} - enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths) { native_paths->include_dirs_ptr = nullptr; native_paths->include_dirs_len = 0; diff --git a/src/stage2.h b/src/stage2.h index e6db241e88..96222e3138 100644 --- a/src/stage2.h +++ b/src/stage2.h @@ -103,6 +103,8 @@ enum Error { ErrorWindowsSdkNotFound, ErrorUnknownDynamicLinkerPath, ErrorTargetHasNoDynamicLinker, + ErrorInvalidAbiVersion, + ErrorInvalidOperatingSystemVersion, }; // ABI warning @@ -290,14 +292,12 @@ struct ZigTarget { const char *llvm_cpu_name; const char *llvm_cpu_features; - const char *builtin_str; + const char *cpu_builtin_str; const char *cache_hash; + const char *os_builtin_str; + const char *dynamic_linker; }; -// ABI warning -ZIG_EXTERN_C enum Error stage2_detect_dynamic_linker(const struct ZigTarget *target, - char **out_ptr, size_t *out_len); - // ABI warning ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu); diff --git a/test/stage1/behavior/asm.zig b/test/stage1/behavior/asm.zig index d1404c6f45..170ad3325d 100644 --- a/test/stage1/behavior/asm.zig +++ b/test/stage1/behavior/asm.zig @@ -1,9 +1,10 @@ const std = @import("std"); -const config = @import("builtin"); const expect = std.testing.expect; +const is_x86_64_linux = std.Target.current.cpu.arch == .x86_64 and std.Target.current.os.tag == .linux; + comptime { - if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { + if (is_x86_64_linux) { asm ( \\.globl this_is_my_alias; \\.type this_is_my_alias, @function; @@ -13,7 +14,7 @@ comptime { } test "module level assembly" { - if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { + if (is_x86_64_linux) { expect(this_is_my_alias() == 1234); } } diff --git a/test/stage1/behavior/byteswap.zig b/test/stage1/behavior/byteswap.zig index c799ba4849..d6e07e7a56 100644 --- a/test/stage1/behavior/byteswap.zig +++ b/test/stage1/behavior/byteswap.zig @@ -1,6 +1,5 @@ const std = @import("std"); const expect = std.testing.expect; -const builtin = @import("builtin"); test "@byteSwap integers" { const ByteSwapIntTest = struct { @@ -41,10 +40,10 @@ test "@byteSwap integers" { test "@byteSwap vectors" { // https://github.com/ziglang/zig/issues/3563 - if (builtin.os == .dragonfly) return error.SkipZigTest; + if (std.Target.current.os.tag == .dragonfly) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/3317 - if (builtin.arch == .mipsel) return error.SkipZigTest; + if (std.Target.current.cpu.arch == .mipsel) return error.SkipZigTest; const ByteSwapVectorTest = struct { fn run() void { diff --git a/test/stage1/behavior/namespace_depends_on_compile_var.zig b/test/stage1/behavior/namespace_depends_on_compile_var.zig index 4c4fc4eefe..8c5c19d733 100644 --- a/test/stage1/behavior/namespace_depends_on_compile_var.zig +++ b/test/stage1/behavior/namespace_depends_on_compile_var.zig @@ -1,5 +1,5 @@ -const builtin = @import("builtin"); -const expect = @import("std").testing.expect; +const std = @import("std"); +const expect = std.testing.expect; test "namespace depends on compile var" { if (some_namespace.a_bool) { @@ -8,7 +8,7 @@ test "namespace depends on compile var" { expect(!some_namespace.a_bool); } } -const some_namespace = switch (builtin.os) { - builtin.Os.linux => @import("namespace_depends_on_compile_var/a.zig"), +const some_namespace = switch (std.builtin.os.tag) { + .linux => @import("namespace_depends_on_compile_var/a.zig"), else => @import("namespace_depends_on_compile_var/b.zig"), }; diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig index e89399c5e2..01e5ac1fb8 100644 --- a/test/stage1/behavior/vector.zig +++ b/test/stage1/behavior/vector.zig @@ -2,7 +2,6 @@ const std = @import("std"); const mem = std.mem; const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; -const builtin = @import("builtin"); test "implicit cast vector to array - bool" { const S = struct { @@ -114,7 +113,7 @@ test "array to vector" { test "vector casts of sizes not divisable by 8" { // https://github.com/ziglang/zig/issues/3563 - if (builtin.os == .dragonfly) return error.SkipZigTest; + if (std.Target.current.os.tag == .dragonfly) return error.SkipZigTest; const S = struct { fn doTheTest() void { diff --git a/test/tests.zig b/test/tests.zig index 60008a2bc6..c088aafc5f 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -31,7 +31,7 @@ pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTransla pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext; const TestTarget = struct { - target: Target = .Native, + target: build.Target = .Native, mode: builtin.Mode = .Debug, link_libc: bool = false, single_threaded: bool = false, From d4f375c46be2e509ee9161b0577d8a25d6620b3e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 25 Feb 2020 02:52:49 -0500 Subject: [PATCH 39/99] stage1: remove get_self_libc_path and glibc_detect_native_version --- src/compiler.cpp | 25 ------------------------- src/compiler.hpp | 1 - src/glibc.cpp | 37 ------------------------------------- src/glibc.hpp | 3 --- 4 files changed, 66 deletions(-) diff --git a/src/compiler.cpp b/src/compiler.cpp index 31bac4ee24..cddecc2025 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -4,31 +4,6 @@ #include -Buf *get_self_libc_path(void) { - static Buf saved_libc_path = BUF_INIT; - static bool searched_for_libc = false; - - for (;;) { - if (saved_libc_path.list.length != 0) { - return &saved_libc_path; - } - if (searched_for_libc) - return nullptr; - ZigList lib_paths = {}; - Error err; - if ((err = os_self_exe_shared_libs(lib_paths))) - return nullptr; - for (size_t i = 0; i < lib_paths.length; i += 1) { - Buf *lib_path = lib_paths.at(i); - if (buf_ends_with_str(lib_path, "libc.so.6")) { - buf_init_from_buf(&saved_libc_path, lib_path); - return &saved_libc_path; - } - } - searched_for_libc = true; - } -} - Error get_compiler_id(Buf **result) { static Buf saved_compiler_id = BUF_INIT; diff --git a/src/compiler.hpp b/src/compiler.hpp index 4a1699b782..47841af5dc 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -12,7 +12,6 @@ #include "error.hpp" Error get_compiler_id(Buf **result); -Buf *get_self_libc_path(void); Buf *get_zig_lib_dir(void); Buf *get_zig_special_dir(Buf *zig_lib_dir); diff --git a/src/glibc.cpp b/src/glibc.cpp index 849aac6c77..91e2f9dfc1 100644 --- a/src/glibc.cpp +++ b/src/glibc.cpp @@ -362,43 +362,6 @@ bool eql_glibc_target(const ZigTarget *a, const ZigTarget *b) { a->abi == b->abi; } -#ifdef ZIG_OS_LINUX -#include -Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver) { - Buf *self_libc_path = get_self_libc_path(); - if (self_libc_path == nullptr) { - // TODO There is still more we could do to detect the native glibc version. For example, - // we could look at the ELF file of `/usr/bin/env`, find `libc.so.6`, and then `readlink` - // to find out the glibc version. This is relevant for the static zig builds distributed - // on the download page, since the above detection based on zig's own dynamic linking - // will not work. - - return ErrorUnknownABI; - } - Buf *link_name = buf_alloc(); - buf_resize(link_name, 4096); - ssize_t amt = readlink(buf_ptr(self_libc_path), buf_ptr(link_name), buf_len(link_name)); - if (amt == -1) { - return ErrorUnknownABI; - } - buf_resize(link_name, amt); - if (!buf_starts_with_str(link_name, "libc-") || !buf_ends_with_str(link_name, ".so")) { - return ErrorUnknownABI; - } - // example: "libc-2.3.4.so" - // example: "libc-2.27.so" - buf_resize(link_name, buf_len(link_name) - 3); // chop off ".so" - glibc_ver->major = 2; - glibc_ver->minor = 0; - glibc_ver->patch = 0; - return target_parse_glibc_version(glibc_ver, buf_ptr(link_name) + 5); -} -#else -Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver) { - return ErrorUnknownABI; -} -#endif - size_t glibc_lib_count(void) { return array_length(glibc_libs); } diff --git a/src/glibc.hpp b/src/glibc.hpp index 42c2099371..8e4c7888ad 100644 --- a/src/glibc.hpp +++ b/src/glibc.hpp @@ -43,9 +43,6 @@ Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbo Error glibc_build_dummies_and_maps(CodeGen *codegen, const ZigGLibCAbi *glibc_abi, const ZigTarget *target, Buf **out_dir, bool verbose, Stage2ProgressNode *progress_node); -// returns ErrorUnknownABI when glibc is not the native libc -Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver); - size_t glibc_lib_count(void); const ZigGLibCLib *glibc_lib_enum(size_t index); const ZigGLibCLib *glibc_lib_find(const char *name); From 87b9e744dda465ecf7663e2463066ea26a44e63a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 25 Feb 2020 03:43:21 -0500 Subject: [PATCH 40/99] update std lib to new Target API --- lib/std/build.zig | 81 ++++++++++- lib/std/build/translate_c.zig | 4 +- lib/std/fmt/parse_float.zig | 2 +- lib/std/io/test.zig | 2 +- lib/std/math/fabs.zig | 2 +- lib/std/math/isinf.zig | 6 +- lib/std/math/isnan.zig | 2 +- lib/std/os.zig | 4 +- lib/std/special/compiler_rt/addXf3_test.zig | 4 +- lib/std/special/compiler_rt/fixtfdi_test.zig | 2 +- lib/std/special/compiler_rt/fixtfsi_test.zig | 2 +- lib/std/special/compiler_rt/fixtfti_test.zig | 2 +- .../special/compiler_rt/fixunstfdi_test.zig | 2 +- .../special/compiler_rt/fixunstfsi_test.zig | 2 +- .../special/compiler_rt/fixunstfti_test.zig | 2 +- .../special/compiler_rt/floattitf_test.zig | 2 +- .../special/compiler_rt/floatuntitf_test.zig | 2 +- lib/std/special/compiler_rt/mulXf3_test.zig | 2 +- .../special/compiler_rt/truncXfYf2_test.zig | 4 +- lib/std/target.zig | 118 +++++----------- src-self-hosted/libc_installation.zig | 14 +- test/assemble_and_link.zig | 4 +- test/compile_errors.zig | 11 +- test/src/translate_c.zig | 4 +- test/stack_traces.zig | 77 ++++++----- test/stage1/behavior/math.zig | 20 +-- test/standalone.zig | 4 +- test/tests.zig | 127 +++++++++--------- test/translate_c.zig | 34 ++--- 29 files changed, 281 insertions(+), 261 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 6c89e7b9d1..25f7d536b1 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -971,7 +971,7 @@ pub const Builder = struct { }; test "builder.findProgram compiles" { - var buf: [1000]u8 = undefined; + var buf: [50000]u8 = undefined; var fba = std.heap.FixedBufferAllocator.init(&buf); const builder = try Builder.create(&fba.allocator, "zig", "zig-cache", "zig-cache"); defer builder.destroy(); @@ -1011,6 +1011,77 @@ pub const Target = union(enum) { pub fn getArch(self: Target) std.Target.Cpu.Arch { return self.getCpu().arch; } + + pub fn isFreeBSD(self: Target) bool { + return self.getTarget().os.tag == .freebsd; + } + + pub fn isDarwin(self: Target) bool { + return self.getTarget().os.tag.isDarwin(); + } + + pub fn isNetBSD(self: Target) bool { + return self.getTarget().os.tag == .netbsd; + } + + pub fn isUefi(self: Target) bool { + return self.getTarget().os.tag == .uefi; + } + + pub fn isDragonFlyBSD(self: Target) bool { + return self.getTarget().os.tag == .dragonfly; + } + + pub fn isLinux(self: Target) bool { + return self.getTarget().os.tag == .linux; + } + + pub fn isWindows(self: Target) bool { + return self.getTarget().os.tag == .windows; + } + + pub fn oFileExt(self: Target) []const u8 { + return self.getTarget().oFileExt(); + } + + pub fn exeFileExt(self: Target) []const u8 { + return self.getTarget().exeFileExt(); + } + + pub fn staticLibSuffix(self: Target) []const u8 { + return self.getTarget().staticLibSuffix(); + } + + pub fn libPrefix(self: Target) []const u8 { + return self.getTarget().libPrefix(); + } + + pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { + return self.getTarget().zigTriple(allocator); + } + + pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { + return self.getTarget().linuxTriple(allocator); + } + + pub fn wantSharedLibSymLinks(self: Target) bool { + return self.getTarget().wantSharedLibSymLinks(); + } + + pub fn vcpkgTriplet(self: Target, allocator: *mem.Allocator, linkage: std.build.VcpkgLinkage) ![]const u8 { + return self.getTarget().vcpkgTriplet(allocator, linkage); + } + + pub fn getExternalExecutor(self: Target) std.Target.Executor { + switch (self) { + .Native => return .native, + .Cross => |t| return t.getExternalExecutor(), + } + } + + pub fn isGnuLibC(self: Target) bool { + return self.getTarget().isGnuLibC(); + } }; pub const Pkg = struct { @@ -1718,7 +1789,7 @@ pub const LibExeObjStep = struct { .NotFound => return error.VcpkgNotFound, .Found => |root| { const allocator = self.builder.allocator; - const triplet = try Target.vcpkgTriplet(allocator, self.target, linkage); + const triplet = try self.target.vcpkgTriplet(allocator, linkage); defer self.builder.allocator.free(triplet); const include_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "include" }); @@ -1944,7 +2015,7 @@ pub const LibExeObjStep = struct { if (populated_cpu_features.eql(cross.cpu.features)) { // The CPU name alone is sufficient. // If it is the baseline CPU, no command line args are required. - if (cross.cpu.model != Target.Cpu.baseline(self.target.getArch()).model) { + if (cross.cpu.model != std.Target.Cpu.baseline(self.target.getArch()).model) { try zig_args.append("-mcpu"); try zig_args.append(cross.cpu.model.name); } @@ -1953,7 +2024,7 @@ pub const LibExeObjStep = struct { try mcpu_buffer.append(cross.cpu.model.name); for (all_features) |feature, i_usize| { - const i = @intCast(Target.Cpu.Feature.Set.Index, i_usize); + const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); const in_cpu_set = populated_cpu_features.isEnabled(i); const in_actual_set = cross.cpu.features.isEnabled(i); if (in_cpu_set and !in_actual_set) { @@ -2001,7 +2072,7 @@ pub const LibExeObjStep = struct { } else switch (self.target.getExternalExecutor()) { .native, .unavailable => {}, .qemu => |bin_name| if (self.enable_qemu) qemu: { - const need_cross_glibc = self.target.isGnu() and self.target.isLinux() and self.is_linking_libc; + const need_cross_glibc = self.target.isGnuLibC() and self.is_linking_libc; const glibc_dir_arg = if (need_cross_glibc) self.glibc_multi_install_dir orelse break :qemu else diff --git a/lib/std/build/translate_c.zig b/lib/std/build/translate_c.zig index 3d1bdad677..90e95be5e1 100644 --- a/lib/std/build/translate_c.zig +++ b/lib/std/build/translate_c.zig @@ -14,7 +14,7 @@ pub const TranslateCStep = struct { source: build.FileSource, output_dir: ?[]const u8, out_basename: []const u8, - target: std.Target = .Native, + target: build.Target = .Native, pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep { const self = builder.allocator.create(TranslateCStep) catch unreachable; @@ -39,7 +39,7 @@ pub const TranslateCStep = struct { ) catch unreachable; } - pub fn setTarget(self: *TranslateCStep, target: std.Target) void { + pub fn setTarget(self: *TranslateCStep, target: build.Target) void { self.target = target; } diff --git a/lib/std/fmt/parse_float.zig b/lib/std/fmt/parse_float.zig index aa6e414336..c62d614a6f 100644 --- a/lib/std/fmt/parse_float.zig +++ b/lib/std/fmt/parse_float.zig @@ -382,7 +382,7 @@ pub fn parseFloat(comptime T: type, s: []const u8) !T { } test "fmt.parseFloat" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/io/test.zig b/lib/std/io/test.zig index dae8940016..f1840b49e3 100644 --- a/lib/std/io/test.zig +++ b/lib/std/io/test.zig @@ -544,7 +544,7 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: } test "Serializer/Deserializer generic" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/math/fabs.zig b/lib/std/math/fabs.zig index 61692283e6..2635962fcc 100644 --- a/lib/std/math/fabs.zig +++ b/lib/std/math/fabs.zig @@ -95,7 +95,7 @@ test "math.fabs64.special" { } test "math.fabs128.special" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/math/isinf.zig b/lib/std/math/isinf.zig index eeac61915c..c51747fd12 100644 --- a/lib/std/math/isinf.zig +++ b/lib/std/math/isinf.zig @@ -74,7 +74,7 @@ pub fn isNegativeInf(x: var) bool { } test "math.isInf" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } @@ -97,7 +97,7 @@ test "math.isInf" { } test "math.isPositiveInf" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } @@ -120,7 +120,7 @@ test "math.isPositiveInf" { } test "math.isNegativeInf" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/math/isnan.zig b/lib/std/math/isnan.zig index 4b7e69490a..2879c44502 100644 --- a/lib/std/math/isnan.zig +++ b/lib/std/math/isnan.zig @@ -16,7 +16,7 @@ pub fn isSignalNan(x: var) bool { } test "math.isNan" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/os.zig b/lib/std/os.zig index 6e96413d78..9f349e7dc4 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -650,7 +650,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void { /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) WriteError!void { - if (comptime std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { return windows.WriteFile(fd, bytes, offset); } @@ -739,7 +739,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void } } - if (comptime std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { var off = offset; for (iov) |item| { try pwrite(fd, item.iov_base[0..item.iov_len], off); diff --git a/lib/std/special/compiler_rt/addXf3_test.zig b/lib/std/special/compiler_rt/addXf3_test.zig index 402bb5a43c..d7e175045c 100644 --- a/lib/std/special/compiler_rt/addXf3_test.zig +++ b/lib/std/special/compiler_rt/addXf3_test.zig @@ -31,7 +31,7 @@ fn test__addtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) void { } test "addtf3" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } @@ -75,7 +75,7 @@ fn test__subtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) void { } test "subtf3" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/fixtfdi_test.zig b/lib/std/special/compiler_rt/fixtfdi_test.zig index 4c43c90550..cb4b94c5cd 100644 --- a/lib/std/special/compiler_rt/fixtfdi_test.zig +++ b/lib/std/special/compiler_rt/fixtfdi_test.zig @@ -11,7 +11,7 @@ fn test__fixtfdi(a: f128, expected: i64) void { } test "fixtfdi" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/fixtfsi_test.zig b/lib/std/special/compiler_rt/fixtfsi_test.zig index 4eabd0c594..96bb151e80 100644 --- a/lib/std/special/compiler_rt/fixtfsi_test.zig +++ b/lib/std/special/compiler_rt/fixtfsi_test.zig @@ -11,7 +11,7 @@ fn test__fixtfsi(a: f128, expected: i32) void { } test "fixtfsi" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/fixtfti_test.zig b/lib/std/special/compiler_rt/fixtfti_test.zig index acda2f162b..73cc0596e7 100644 --- a/lib/std/special/compiler_rt/fixtfti_test.zig +++ b/lib/std/special/compiler_rt/fixtfti_test.zig @@ -11,7 +11,7 @@ fn test__fixtfti(a: f128, expected: i128) void { } test "fixtfti" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/fixunstfdi_test.zig b/lib/std/special/compiler_rt/fixunstfdi_test.zig index 154fffe18a..02cef2f700 100644 --- a/lib/std/special/compiler_rt/fixunstfdi_test.zig +++ b/lib/std/special/compiler_rt/fixunstfdi_test.zig @@ -7,7 +7,7 @@ fn test__fixunstfdi(a: f128, expected: u64) void { } test "fixunstfdi" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/fixunstfsi_test.zig b/lib/std/special/compiler_rt/fixunstfsi_test.zig index af312ddc46..734efff4de 100644 --- a/lib/std/special/compiler_rt/fixunstfsi_test.zig +++ b/lib/std/special/compiler_rt/fixunstfsi_test.zig @@ -9,7 +9,7 @@ fn test__fixunstfsi(a: f128, expected: u32) void { const inf128 = @bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)); test "fixunstfsi" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/fixunstfti_test.zig b/lib/std/special/compiler_rt/fixunstfti_test.zig index 84dbf991e2..649fcdf1e3 100644 --- a/lib/std/special/compiler_rt/fixunstfti_test.zig +++ b/lib/std/special/compiler_rt/fixunstfti_test.zig @@ -9,7 +9,7 @@ fn test__fixunstfti(a: f128, expected: u128) void { const inf128 = @bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)); test "fixunstfti" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/floattitf_test.zig b/lib/std/special/compiler_rt/floattitf_test.zig index 0b2b5b958a..4601b90107 100644 --- a/lib/std/special/compiler_rt/floattitf_test.zig +++ b/lib/std/special/compiler_rt/floattitf_test.zig @@ -7,7 +7,7 @@ fn test__floattitf(a: i128, expected: f128) void { } test "floattitf" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/floatuntitf_test.zig b/lib/std/special/compiler_rt/floatuntitf_test.zig index 8b99bbef5d..34c7407c98 100644 --- a/lib/std/special/compiler_rt/floatuntitf_test.zig +++ b/lib/std/special/compiler_rt/floatuntitf_test.zig @@ -7,7 +7,7 @@ fn test__floatuntitf(a: u128, expected: f128) void { } test "floatuntitf" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/mulXf3_test.zig b/lib/std/special/compiler_rt/mulXf3_test.zig index 00db984a89..45baa62a17 100644 --- a/lib/std/special/compiler_rt/mulXf3_test.zig +++ b/lib/std/special/compiler_rt/mulXf3_test.zig @@ -44,7 +44,7 @@ fn makeNaN128(rand: u64) f128 { return float_result; } test "multf3" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/special/compiler_rt/truncXfYf2_test.zig b/lib/std/special/compiler_rt/truncXfYf2_test.zig index f14dbe6b43..bd05c8652c 100644 --- a/lib/std/special/compiler_rt/truncXfYf2_test.zig +++ b/lib/std/special/compiler_rt/truncXfYf2_test.zig @@ -151,7 +151,7 @@ fn test__trunctfsf2(a: f128, expected: u32) void { } test "trunctfsf2" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } @@ -190,7 +190,7 @@ fn test__trunctfdf2(a: f128, expected: u64) void { } test "trunctfdf2" { - if (@import("std").Target.current.isWindows()) { + if (@import("std").Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } diff --git a/lib/std/target.zig b/lib/std/target.zig index c9f8a247fe..9007771c1a 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -53,6 +53,13 @@ pub const Target = struct { emscripten, uefi, other, + + pub fn isDarwin(tag: Tag) bool { + return switch (tag) { + .ios, .macosx, .watchos, .tvos => true, + else => false, + }; + } }; /// Based on NTDDI version constants from @@ -921,7 +928,7 @@ pub const Target = struct { } /// Returned slice must be freed by the caller. - pub fn vcpkgTriplet(allocator: *mem.Allocator, target: Target, linkage: std.build.VcpkgLinkage) ![]const u8 { + pub fn vcpkgTriplet(target: Target, allocator: *mem.Allocator, linkage: std.build.VcpkgLinkage) ![]const u8 { const arch = switch (target.cpu.arch) { .i386 => "x86", .x86_64 => "x64", @@ -960,14 +967,6 @@ pub const Target = struct { return self.zigTriple(allocator); } - pub fn zigTripleNoSubArch(self: Target, allocator: *mem.Allocator) ![]u8 { - return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.cpu.arch), - @tagName(self.os.tag), - @tagName(self.abi), - }); - } - pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ @tagName(self.cpu.arch), @@ -1111,11 +1110,11 @@ pub const Target = struct { } pub fn exeFileExt(self: Target) []const u8 { - if (self.isWindows()) { + if (self.os.tag == .windows) { return ".exe"; - } else if (self.isUefi()) { + } else if (self.os.tag == .uefi) { return ".efi"; - } else if (self.isWasm()) { + } else if (self.cpu.arch.isWasm()) { return ".wasm"; } else { return ""; @@ -1123,7 +1122,7 @@ pub const Target = struct { } pub fn staticLibSuffix(self: Target) []const u8 { - if (self.isWasm()) { + if (self.cpu.arch.isWasm()) { return ".wasm"; } switch (self.abi) { @@ -1143,7 +1142,7 @@ pub const Target = struct { } pub fn libPrefix(self: Target) []const u8 { - if (self.isWasm()) { + if (self.cpu.arch.isWasm()) { return ""; } switch (self.abi) { @@ -1153,19 +1152,19 @@ pub const Target = struct { } pub fn getObjectFormat(self: Target) ObjectFormat { - if (self.isWindows() or self.isUefi()) { + if (self.os.tag == .windows or self.os.tag == .uefi) { return .coff; } else if (self.isDarwin()) { return .macho; } - if (self.isWasm()) { + if (self.cpu.arch.isWasm()) { return .wasm; } return .elf; } pub fn isMinGW(self: Target) bool { - return self.isWindows() and self.isGnu(); + return self.os.tag == .windows and self.isGnu(); } pub fn isGnu(self: Target) bool { @@ -1176,27 +1175,6 @@ pub const Target = struct { return self.abi.isMusl(); } - pub fn isDarwin(self: Target) bool { - return switch (self.os.tag) { - .ios, .macosx, .watchos, .tvos => true, - else => false, - }; - } - - pub fn isWindows(self: Target) bool { - return switch (self.os.tag) { - .windows => true, - else => false, - }; - } - - pub fn isLinux(self: Target) bool { - return switch (self.os.tag) { - .linux => true, - else => false, - }; - } - pub fn isAndroid(self: Target) bool { return switch (self.abi) { .android => true, @@ -1204,36 +1182,12 @@ pub const Target = struct { }; } - pub fn isDragonFlyBSD(self: Target) bool { - return switch (self.os.tag) { - .dragonfly => true, - else => false, - }; - } - - pub fn isUefi(self: Target) bool { - return switch (self.os.tag) { - .uefi => true, - else => false, - }; - } - pub fn isWasm(self: Target) bool { return self.cpu.arch.isWasm(); } - pub fn isFreeBSD(self: Target) bool { - return switch (self.os.tag) { - .freebsd => true, - else => false, - }; - } - - pub fn isNetBSD(self: Target) bool { - return switch (self.os.tag) { - .netbsd => true, - else => false, - }; + pub fn isDarwin(self: Target) bool { + return self.os.tag.isDarwin(); } pub fn isGnuLibC(self: Target) bool { @@ -1241,11 +1195,11 @@ pub const Target = struct { } pub fn wantSharedLibSymLinks(self: Target) bool { - return !self.isWindows(); + return self.os.tag != .windows; } pub fn osRequiresLibC(self: Target) bool { - return self.isDarwin() or self.isFreeBSD() or self.isNetBSD(); + return self.isDarwin() or self.os.tag == .freebsd or self.os.tag == .netbsd; } pub fn getArchPtrBitWidth(self: Target) u32 { @@ -1309,7 +1263,7 @@ pub const Target = struct { } pub fn supportsNewStackCall(self: Target) bool { - return !self.isWasm(); + return !self.cpu.arch.isWasm(); } pub const Executor = union(enum) { @@ -1321,8 +1275,6 @@ pub const Target = struct { }; pub fn getExternalExecutor(self: Target) Executor { - if (@as(@TagType(Target), self) == .Native) return .native; - // If the target OS matches the host OS, we can use QEMU to emulate a foreign architecture. if (self.os.tag == builtin.os.tag) { return switch (self.cpu.arch) { @@ -1347,22 +1299,18 @@ pub const Target = struct { }; } - if (self.isWindows()) { - switch (self.getArchPtrBitWidth()) { + switch (self.os.tag) { + .windows => switch (self.getArchPtrBitWidth()) { 32 => return Executor{ .wine = "wine" }, 64 => return Executor{ .wine = "wine64" }, else => return .unavailable, - } - } - - if (self.os == .wasi) { - switch (self.getArchPtrBitWidth()) { + }, + .wasi => switch (self.getArchPtrBitWidth()) { 32 => return Executor{ .wasmtime = "wasmtime" }, else => return .unavailable, - } + }, + else => return .unavailable, } - - return .unavailable; } pub const FloatAbi = enum { @@ -1566,12 +1514,12 @@ test "Target.parse" { std.testing.expect(target.cpu.arch == .aarch64); std.testing.expect(target.os.tag == .linux); - std.testing.expect(target.os.version_range.linux.min.major == 3); - std.testing.expect(target.os.version_range.linux.min.minor == 10); - std.testing.expect(target.os.version_range.linux.min.patch == 0); - std.testing.expect(target.os.version_range.linux.max.major == 4); - std.testing.expect(target.os.version_range.linux.max.minor == 4); - std.testing.expect(target.os.version_range.linux.max.patch == 1); + std.testing.expect(target.os.version_range.linux.range.min.major == 3); + std.testing.expect(target.os.version_range.linux.range.min.minor == 10); + std.testing.expect(target.os.version_range.linux.range.min.patch == 0); + std.testing.expect(target.os.version_range.linux.range.max.major == 4); + std.testing.expect(target.os.version_range.linux.range.max.minor == 4); + std.testing.expect(target.os.version_range.linux.range.max.patch == 1); std.testing.expect(target.os.version_range.linux.glibc.major == 2); std.testing.expect(target.os.version_range.linux.glibc.minor == 27); std.testing.expect(target.os.version_range.linux.glibc.patch == 0); diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index d617abb821..41def38126 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -7,11 +7,7 @@ const Allocator = std.mem.Allocator; const Batch = std.event.Batch; const is_darwin = Target.current.isDarwin(); -const is_windows = Target.current.isWindows(); -const is_freebsd = Target.current.isFreeBSD(); -const is_netbsd = Target.current.isNetBSD(); -const is_linux = Target.current.isLinux(); -const is_dragonfly = Target.current.isDragonFlyBSD(); +const is_windows = Target.current.os.tag == .windows; const is_gnu = Target.current.isGnu(); usingnamespace @import("windows_sdk.zig"); @@ -216,10 +212,10 @@ pub const LibCInstallation = struct { var batch = Batch(FindError!void, 2, .auto_async).init(); errdefer batch.wait() catch {}; batch.add(&async self.findNativeIncludeDirPosix(args)); - if (is_freebsd or is_netbsd) { - self.crt_dir = try std.mem.dupeZ(args.allocator, u8, "/usr/lib"); - } else if (is_linux or is_dragonfly) { - batch.add(&async self.findNativeCrtDirPosix(args)); + switch (Target.current.os.tag) { + .freebsd, .netbsd => self.crt_dir = try std.mem.dupeZ(args.allocator, u8, "/usr/lib"), + .linux, .dragonfly => batch.add(&async self.findNativeCrtDirPosix(args)), + else => {}, } break :blk batch.wait(); }; diff --git a/test/assemble_and_link.zig b/test/assemble_and_link.zig index 8c727e87b5..86209bd034 100644 --- a/test/assemble_and_link.zig +++ b/test/assemble_and_link.zig @@ -1,8 +1,8 @@ -const builtin = @import("builtin"); +const std = @import("std"); const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { - if (builtin.os == builtin.Os.linux and builtin.arch == builtin.Arch.x86_64) { + if (std.Target.current.os.tag == .linux and std.Target.current.cpu.arch == .x86_64) { cases.addAsm("hello world linux x86_64", \\.text \\.globl _start diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 91c17d8807..7c1eb6d409 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,5 +1,4 @@ const tests = @import("tests.zig"); -const builtin = @import("builtin"); const Target = @import("std").Target; pub fn addCases(cases: *tests.CompileErrorContext) void { @@ -387,10 +386,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , &[_][]const u8{ "tmp.zig:3:5: error: target arch 'wasm32' does not support calling with a new stack", }); - tc.target = Target{ + tc.target = tests.Target{ .Cross = .{ .cpu = Target.Cpu.baseline(.wasm32), - .os = .wasi, + .os = Target.Os.defaultVersionRange(.wasi), .abi = .none, }, }; @@ -788,10 +787,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , &[_][]const u8{ "tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs", }); - tc.target = Target{ + tc.target = tests.Target{ .Cross = .{ .cpu = Target.Cpu.baseline(.x86_64), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .gnu, }, }; @@ -1453,7 +1452,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:2:18: error: invalid operands to binary expression: 'error{A}' and 'error{B}'", }); - if (builtin.os == builtin.Os.linux) { + if (Target.current.os.tag == .linux) { cases.addTest("implicit dependency on libc", \\extern "c" fn exit(u8) void; \\export fn entry() void { diff --git a/test/src/translate_c.zig b/test/src/translate_c.zig index 21aa92537c..968f09eeb8 100644 --- a/test/src/translate_c.zig +++ b/test/src/translate_c.zig @@ -19,7 +19,7 @@ pub const TranslateCContext = struct { sources: ArrayList(SourceFile), expected_lines: ArrayList([]const u8), allow_warnings: bool, - target: std.Target = .Native, + target: build.Target = .Native, const SourceFile = struct { filename: []const u8, @@ -75,7 +75,7 @@ pub const TranslateCContext = struct { pub fn addWithTarget( self: *TranslateCContext, name: []const u8, - target: std.Target, + target: build.Target, source: []const u8, expected_lines: []const []const u8, ) void { diff --git a/test/stack_traces.zig b/test/stack_traces.zig index fd4ff69964..ab1156c3cb 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -1,4 +1,3 @@ -const builtin = @import("builtin"); const std = @import("std"); const os = std.os; const tests = @import("tests.zig"); @@ -43,32 +42,32 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\} ; - switch (builtin.os) { + switch (builtin.os.tag) { .freebsd => { cases.addCase( "return", source_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in main (test) \\ return error.TheSkyIsFalling; \\ ^ \\ , // release-safe - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in std.start.main (test) \\ return error.TheSkyIsFalling; \\ ^ \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -77,7 +76,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { source_try_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in foo (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -87,7 +86,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-safe - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in std.start.main (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -97,11 +96,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -110,7 +109,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { source_try_try_return_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:12:5: [address] in make_error (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -126,7 +125,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-safe - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:12:5: [address] in std.start.main (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -142,11 +141,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -157,25 +156,25 @@ pub fn addCases(cases: *tests.StackTracesContext) void { source_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in main (test) \\ return error.TheSkyIsFalling; \\ ^ \\ , // release-safe - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in std.start.posixCallMainAndExit (test) \\ return error.TheSkyIsFalling; \\ ^ \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -184,7 +183,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { source_try_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in foo (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -194,7 +193,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-safe - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in std.start.posixCallMainAndExit (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -204,11 +203,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -217,7 +216,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { source_try_try_return_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:12:5: [address] in make_error (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -233,7 +232,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-safe - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:12:5: [address] in std.start.posixCallMainAndExit (test) \\ return error.TheSkyIsFalling; \\ ^ @@ -249,11 +248,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -278,11 +277,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -311,11 +310,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -356,11 +355,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ , // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -371,7 +370,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { source_return, [_][]const u8{ // debug - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\source.zig:4:5: [address] in main (test.obj) \\ return error.TheSkyIsFalling; \\ ^ @@ -381,11 +380,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // --disabled-- results in segmenetation fault "", // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -407,11 +406,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // --disabled-- results in segmenetation fault "", // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); @@ -439,11 +438,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // --disabled-- results in segmenetation fault "", // release-fast - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ , // release-small - \\error: TheSkyIsFalling + \\error: TheSkyIsFalling \\ }, ); diff --git a/test/stage1/behavior/math.zig b/test/stage1/behavior/math.zig index 65a7d9e1f1..e657b5472b 100644 --- a/test/stage1/behavior/math.zig +++ b/test/stage1/behavior/math.zig @@ -529,7 +529,7 @@ test "comptime_int xor" { } test "f128" { - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } @@ -631,7 +631,7 @@ test "NaN comparison" { // TODO: https://github.com/ziglang/zig/issues/3338 return error.SkipZigTest; } - if (std.Target.current.isWindows()) { + if (std.Target.current.os.tag == .windows) { // TODO https://github.com/ziglang/zig/issues/508 return error.SkipZigTest; } @@ -666,14 +666,14 @@ test "128-bit multiplication" { test "vector comparison" { const S = struct { fn doTheTest() void { - var a: @Vector(6, i32) = [_]i32{1, 3, -1, 5, 7, 9}; - var b: @Vector(6, i32) = [_]i32{-1, 3, 0, 6, 10, -10}; - expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{false, false, true, true, true, false})); - expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{false, true, true, true, true, false})); - expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{false, true, false, false, false, false})); - expect(mem.eql(bool, &@as([6]bool, a != b), &[_]bool{true, false, true, true, true, true})); - expect(mem.eql(bool, &@as([6]bool, a > b), &[_]bool{true, false, false, false, false, true})); - expect(mem.eql(bool, &@as([6]bool, a >= b), &[_]bool{true, true, false, false, false, true})); + var a: @Vector(6, i32) = [_]i32{ 1, 3, -1, 5, 7, 9 }; + var b: @Vector(6, i32) = [_]i32{ -1, 3, 0, 6, 10, -10 }; + expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{ false, false, true, true, true, false })); + expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{ false, true, true, true, true, false })); + expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{ false, true, false, false, false, false })); + expect(mem.eql(bool, &@as([6]bool, a != b), &[_]bool{ true, false, true, true, true, true })); + expect(mem.eql(bool, &@as([6]bool, a > b), &[_]bool{ true, false, false, false, false, true })); + expect(mem.eql(bool, &@as([6]bool, a >= b), &[_]bool{ true, true, false, false, false, true })); } }; S.doTheTest(); diff --git a/test/standalone.zig b/test/standalone.zig index 2c5b9c790e..aec9c82726 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -18,10 +18,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.addBuildFile("test/standalone/use_alias/build.zig"); cases.addBuildFile("test/standalone/brace_expansion/build.zig"); cases.addBuildFile("test/standalone/empty_env/build.zig"); - if (std.Target.current.getOs() != .wasi) { + if (std.Target.current.os.tag != .wasi) { cases.addBuildFile("test/standalone/load_dynamic_library/build.zig"); } - if (std.Target.current.getArch() == .x86_64) { // TODO add C ABI support for other architectures + if (std.Target.current.cpu.arch == .x86_64) { // TODO add C ABI support for other architectures cases.addBuildFile("test/stage1/c_abi/build.zig"); } } diff --git a/test/tests.zig b/test/tests.zig index c088aafc5f..78eaf56273 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1,16 +1,15 @@ const std = @import("std"); +const builtin = std.builtin; const debug = std.debug; const warn = debug.warn; const build = std.build; pub const Target = build.Target; -pub const CrossTarget = build.CrossTarget; const Buffer = std.Buffer; const io = std.io; const fs = std.fs; const mem = std.mem; const fmt = std.fmt; const ArrayList = std.ArrayList; -const builtin = @import("builtin"); const Mode = builtin.Mode; const LibExeObjStep = build.LibExeObjStep; @@ -54,18 +53,18 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.x86_64), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.x86_64), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, }, TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.x86_64), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.x86_64), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .gnu, }, }, @@ -73,9 +72,9 @@ const test_targets = blk: { }, TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.x86_64), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.x86_64), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -84,18 +83,18 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.i386), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.i386), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, }, TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.i386), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.i386), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -104,18 +103,18 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.aarch64), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.aarch64), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, }, TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.aarch64), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.aarch64), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -123,9 +122,9 @@ const test_targets = blk: { }, TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.aarch64), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.aarch64), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .gnu, }, }, @@ -133,21 +132,25 @@ const test_targets = blk: { }, TestTarget{ - .target = Target.parse(.{ - .arch_os_abi = "arm-linux-none", - .cpu_features = "generic+v8a", - }) catch unreachable, + .target = .{ + .Cross = std.Target.parse(.{ + .arch_os_abi = "arm-linux-none", + .cpu_features = "generic+v8a", + }) catch unreachable, + }, }, TestTarget{ - .target = Target.parse(.{ - .arch_os_abi = "arm-linux-musleabihf", - .cpu_features = "generic+v8a", - }) catch unreachable, + .target = .{ + .Cross = std.Target.parse(.{ + .arch_os_abi = "arm-linux-musleabihf", + .cpu_features = "generic+v8a", + }) catch unreachable, + }, .link_libc = true, }, // TODO https://github.com/ziglang/zig/issues/3287 //TestTarget{ - // .target = Target.parse(.{ + // .target = std.Target.parse(.{ // .arch_os_abi = "arm-linux-gnueabihf", // .cpu_features = "generic+v8a", // }) catch unreachable, @@ -156,18 +159,18 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.mipsel), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.mipsel), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, }, TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.mipsel), - .os = Target.Os.defaultVersionRange(.linux), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.mipsel), + .os = std.Target.Os.defaultVersionRange(.linux), .abi = .musl, }, }, @@ -176,9 +179,9 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.x86_64), - .os = Target.Os.defaultVersionRange(.macosx), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.x86_64), + .os = std.Target.Os.defaultVersionRange(.macosx), .abi = .gnu, }, }, @@ -188,9 +191,9 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.i386), - .os = Target.Os.defaultVersionRange(.windows), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.i386), + .os = std.Target.Os.defaultVersionRange(.windows), .abi = .msvc, }, }, @@ -198,9 +201,9 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.x86_64), - .os = Target.Os.defaultVersionRange(.windows), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.x86_64), + .os = std.Target.Os.defaultVersionRange(.windows), .abi = .msvc, }, }, @@ -208,9 +211,9 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.i386), - .os = Target.Os.defaultVersionRange(.windows), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.i386), + .os = std.Target.Os.defaultVersionRange(.windows), .abi = .gnu, }, }, @@ -219,9 +222,9 @@ const test_targets = blk: { TestTarget{ .target = Target{ - .Cross = CrossTarget{ - .cpu = Target.Cpu.baseline(.x86_64), - .os = Target.Os.defaultVersionRange(.windows), + .Cross = .{ + .cpu = std.Target.Cpu.baseline(.x86_64), + .os = std.Target.Os.defaultVersionRange(.windows), .abi = .gnu, }, }, @@ -438,7 +441,7 @@ pub fn addPkgTests( if (skip_libc and test_target.link_libc) continue; - if (test_target.link_libc and test_target.target.osRequiresLibC()) { + if (test_target.link_libc and test_target.target.getTarget().osRequiresLibC()) { // This would be a redundant test. continue; } @@ -448,8 +451,8 @@ pub fn addPkgTests( const ArchTag = @TagType(builtin.Arch); if (test_target.disable_native and - test_target.target.getOs() == builtin.os and - test_target.target.getArch() == builtin.arch) + test_target.target.getOs() == std.Target.current.os.tag and + test_target.target.getArch() == std.Target.current.cpu.arch) { continue; } @@ -459,7 +462,7 @@ pub fn addPkgTests( } else false; if (!want_this_mode) continue; - const libc_prefix = if (test_target.target.osRequiresLibC()) + const libc_prefix = if (test_target.target.getTarget().osRequiresLibC()) "" else if (test_target.link_libc) "c" @@ -469,7 +472,7 @@ pub fn addPkgTests( const triple_prefix = if (test_target.target == .Native) @as([]const u8, "native") else - test_target.target.zigTripleNoSubArch(b.allocator) catch unreachable; + test_target.target.zigTriple(b.allocator) catch unreachable; const these_tests = b.addTest(root_src); const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; @@ -660,7 +663,7 @@ pub const StackTracesContext = struct { const delims = [_][]const u8{ ":", ":", ":", " in " }; var marks = [_]usize{0} ** 4; // offset search past `[drive]:` on windows - var pos: usize = if (builtin.os == .windows) 2 else 0; + var pos: usize = if (std.Target.current.os.tag == .windows) 2 else 0; for (delims) |delim, i| { marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse { try buf.append(line); diff --git a/test/translate_c.zig b/test/translate_c.zig index 701513153c..07364fb032 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,6 @@ const tests = @import("tests.zig"); -const builtin = @import("builtin"); -const Target = @import("std").Target; +const std = @import("std"); +const Target = std.Target; pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("macro line continuation", @@ -665,7 +665,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - if (builtin.os != builtin.Os.windows) { + if (Target.current.os.tag != .windows) { // Windows treats this as an enum with type c_int cases.add("big negative enum init values when C ABI supports long long enums", \\enum EnumWithInits { @@ -1064,7 +1064,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - if (builtin.os != builtin.Os.windows) { + if (Target.current.os.tag != .windows) { // sysv_abi not currently supported on windows cases.add("Macro qualified functions", \\void __attribute__((sysv_abi)) foo(void); @@ -1093,10 +1093,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const fn1 = ?fn (u8) callconv(.C) void; }); - cases.addWithTarget("Calling convention", tests.Target{ + cases.addWithTarget("Calling convention", .{ .Cross = .{ .cpu = Target.Cpu.baseline(.i386), - .os = .linux, + .os = Target.Os.defaultVersionRange(.linux), .abi = .none, }, }, @@ -1113,10 +1113,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub fn foo5(a: [*c]f32) callconv(.Thiscall) void; }); - cases.addWithTarget("Calling convention", Target.parse(.{ - .arch_os_abi = "arm-linux-none", - .cpu_features = "generic+v8_5a", - }) catch unreachable, + cases.addWithTarget("Calling convention", .{ + .Cross = Target.parse(.{ + .arch_os_abi = "arm-linux-none", + .cpu_features = "generic+v8_5a", + }) catch unreachable, + }, \\void __attribute__((pcs("aapcs"))) foo1(float *a); \\void __attribute__((pcs("aapcs-vfp"))) foo2(float *a); , &[_][]const u8{ @@ -1124,10 +1126,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub fn foo2(a: [*c]f32) callconv(.AAPCSVFP) void; }); - cases.addWithTarget("Calling convention", Target.parse(.{ - .arch_os_abi = "aarch64-linux-none", - .cpu_features = "generic+v8_5a", - }) catch unreachable, + cases.addWithTarget("Calling convention", .{ + .Cross = Target.parse(.{ + .arch_os_abi = "aarch64-linux-none", + .cpu_features = "generic+v8_5a", + }) catch unreachable, + }, \\void __attribute__((aarch64_vector_pcs)) foo1(float *a); , &[_][]const u8{ \\pub fn foo1(a: [*c]f32) callconv(.Vectorcall) void; @@ -1596,7 +1600,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - if (builtin.os != .windows) { + if (Target.current.os.tag != .windows) { // When clang uses the -windows-none triple it behaves as MSVC and // interprets the inner `struct Bar` as an anonymous structure cases.add("type referenced struct", From dbe4d72bcfb20fc43713781679a0d23aea0a17d9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 01:18:23 -0500 Subject: [PATCH 41/99] separate std.Target and std.zig.CrossTarget Zig now supports a more fine-grained sense of what is native and what is not. Some examples: This is now allowed: -target native Different OS but native CPU, default Windows C ABI: -target native-windows This could be useful for example when running in Wine. Different CPU but native OS, native C ABI. -target x86_64-native -mcpu=skylake Different C ABI but otherwise native target: -target native-native-musl -target native-native-gnu Lots of breaking changes to related std lib APIs. Calls to getOs() will need to be changed to getOsTag(). Calls to getArch() will need to be changed to getCpuArch(). Usage of Target.Cross and Target.Native need to be updated to use CrossTarget API. `std.build.Builder.standardTargetOptions` is changed to accept its parameters as a struct with default values. It now has the ability to specify a whitelist of targets allowed, as well as the default target. Rather than two different ways of collecting the target, it's now always a string that is validated, and prints helpful diagnostics for invalid targets. This feature should now be actually useful, and contributions welcome to further improve the user experience. `std.build.LibExeObjStep.setTheTarget` is removed. `std.build.LibExeObjStep.setTarget` is updated to take a CrossTarget parameter. `std.build.LibExeObjStep.setTargetGLibC` is removed. glibc versions are handled in the CrossTarget API and can be specified with the `-target` triple. `std.builtin.Version` gains a `format` method. --- build.zig | 2 +- doc/docgen.zig | 4 +- lib/std/build.zig | 298 +++++-------- lib/std/build/translate_c.zig | 14 +- lib/std/builtin.zig | 23 + lib/std/target.zig | 695 +++++++++--------------------- lib/std/testing.zig | 10 +- lib/std/zig.zig | 9 +- lib/std/zig/cross_target.zig | 766 ++++++++++++++++++++++++++++++++++ src-self-hosted/stage2.zig | 143 ++++--- test/compile_errors.zig | 24 +- test/src/translate_c.zig | 5 +- test/tests.zig | 236 +++++------ test/translate_c.zig | 36 +- 14 files changed, 1307 insertions(+), 958 deletions(-) create mode 100644 lib/std/zig/cross_target.zig diff --git a/build.zig b/build.zig index e1ae5cf3cd..32694afb0a 100644 --- a/build.zig +++ b/build.zig @@ -298,7 +298,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { } dependOnLib(b, exe, ctx.llvm); - if (exe.target.getOs() == .linux) { + if (exe.target.getOsTag() == .linux) { try addCxxKnownPath(b, ctx, exe, "libstdc++.a", \\Unable to determine path to libstdc++.a \\On Fedora, install libstdc++-static and try again. diff --git a/doc/docgen.zig b/doc/docgen.zig index b429c93e65..9b8aca18d0 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -10,8 +10,8 @@ const testing = std.testing; const max_doc_file_size = 10 * 1024 * 1024; -const exe_ext = @as(std.build.Target, std.build.Target.Native).exeFileExt(); -const obj_ext = @as(std.build.Target, std.build.Target.Native).oFileExt(); +const exe_ext = @as(std.zig.CrossTarget, .{}).exeFileExt(); +const obj_ext = @as(std.zig.CrossTarget, .{}).oFileExt(); const tmp_dir_name = "docgen_tmp"; const test_out_path = tmp_dir_name ++ fs.path.sep_str ++ "test" ++ exe_ext; diff --git a/lib/std/build.zig b/lib/std/build.zig index 25f7d536b1..92b06a0261 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1,5 +1,5 @@ const std = @import("std.zig"); -const builtin = @import("builtin"); +const builtin = std.builtin; const io = std.io; const fs = std.fs; const mem = std.mem; @@ -15,6 +15,7 @@ const BufSet = std.BufSet; const BufMap = std.BufMap; const fmt_lib = std.fmt; const File = std.fs.File; +const CrossTarget = std.zig.CrossTarget; pub const FmtStep = @import("build/fmt.zig").FmtStep; pub const TranslateCStep = @import("build/translate_c.zig").TranslateCStep; @@ -521,24 +522,77 @@ pub const Builder = struct { return mode; } - /// Exposes standard `zig build` options for choosing a target. Pass `null` to support all targets. - pub fn standardTargetOptions(self: *Builder, supported_targets: ?[]const Target) Target { - if (supported_targets) |target_list| { - // TODO detect multiple args and emit an error message - // there's probably a better way to collect the target - for (target_list) |targ| { - const targ_str = targ.zigTriple(self.allocator) catch unreachable; - const targ_desc = targ.allocDescription(self.allocator) catch unreachable; - const this_targ_opt = self.option(bool, targ_str, targ_desc) orelse false; - if (this_targ_opt) { - return targ; + pub const StandardTargetOptionsArgs = struct { + whitelist: ?[]const CrossTarget = null, + + default_target: CrossTarget = .{}, + }; + + /// Exposes standard `zig build` options for choosing a target. + pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget { + const triple = self.option( + []const u8, + "target", + "The Arch, OS, and ABI to build for.", + ) orelse return args.default_target; + + // TODO add cpu and features as part of the target triple + + var diags: std.Target.ParseOptions.Diagnostics = .{}; + const selected_target = CrossTarget.parse(.{ + .arch_os_abi = triple, + .diagnostics = &diags, + }) catch |err| switch (err) { + error.UnknownCpuModel => { + std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{ + diags.cpu_name.?, + @tagName(diags.arch.?), + }); + for (diags.arch.?.allCpuModels()) |cpu| { + std.debug.warn(" {}\n", .{cpu.name}); + } + process.exit(1); + }, + error.UnknownCpuFeature => { + std.debug.warn( + \\Unknown CPU feature: '{}' + \\Available CPU features for architecture '{}': + \\ + , .{ + diags.unknown_feature_name, + @tagName(diags.arch.?), + }); + for (diags.arch.?.allFeaturesList()) |feature| { + std.debug.warn(" {}: {}\n", .{ feature.name, feature.description }); + } + process.exit(1); + }, + else => |e| return e, + }; + + const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable; + + if (args.whitelist) |list| whitelist_check: { + // Make sure it's a match of one of the list. + for (list) |t| { + const t_triple = t.zigTriple(self.allocator) catch unreachable; + if (mem.eql(u8, t_triple, selected_canonicalized_triple)) { + break :whitelist_check; } } - return Target.Native; - } else { - const target_str = self.option([]const u8, "target", "the target to build for") orelse return Target.Native; - return Target.parse(.{ .arch_os_abi = target_str }) catch unreachable; // TODO better error message for bad target + std.debug.warn("Chosen target '{}' does not match one of the supported targets:\n", .{ + selected_canonicalized_triple, + }); + for (list) |t| { + const t_triple = t.zigTriple(self.allocator) catch unreachable; + std.debug.warn(" {}\n", t_triple); + } + // TODO instead of process exit, return error and have a zig build flag implemented by + // the build runner that turns process exits into error return traces + process.exit(1); } + + return selected_target; } pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool { @@ -796,7 +850,7 @@ pub const Builder = struct { pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { // TODO report error for ambiguous situations - const exe_extension = (Target{ .Native = {} }).exeFileExt(); + const exe_extension = @as(CrossTarget, .{}).exeFileExt(); for (self.search_prefixes.toSliceConst()) |search_prefix| { for (names) |name| { if (fs.path.isAbsolute(name)) { @@ -978,111 +1032,11 @@ test "builder.findProgram compiles" { _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; } -/// Deprecated. Use `builtin.Version`. +/// Deprecated. Use `std.builtin.Version`. pub const Version = builtin.Version; -/// Deprecated. Use `std.Target`. -pub const CrossTarget = std.Target; - -/// Wraps `std.Target` so that it can be annotated as "the native target" or an explicitly specified target. -pub const Target = union(enum) { - Native, - Cross: std.Target, - - pub fn getTarget(self: Target) std.Target { - return switch (self) { - .Native => std.Target.current, - .Cross => |t| t, - }; - } - - pub fn getOs(self: Target) std.Target.Os.Tag { - return self.getTarget().os.tag; - } - - pub fn getCpu(self: Target) std.Target.Cpu { - return self.getTarget().cpu; - } - - pub fn getAbi(self: Target) std.Target.Abi { - return self.getTarget().abi; - } - - pub fn getArch(self: Target) std.Target.Cpu.Arch { - return self.getCpu().arch; - } - - pub fn isFreeBSD(self: Target) bool { - return self.getTarget().os.tag == .freebsd; - } - - pub fn isDarwin(self: Target) bool { - return self.getTarget().os.tag.isDarwin(); - } - - pub fn isNetBSD(self: Target) bool { - return self.getTarget().os.tag == .netbsd; - } - - pub fn isUefi(self: Target) bool { - return self.getTarget().os.tag == .uefi; - } - - pub fn isDragonFlyBSD(self: Target) bool { - return self.getTarget().os.tag == .dragonfly; - } - - pub fn isLinux(self: Target) bool { - return self.getTarget().os.tag == .linux; - } - - pub fn isWindows(self: Target) bool { - return self.getTarget().os.tag == .windows; - } - - pub fn oFileExt(self: Target) []const u8 { - return self.getTarget().oFileExt(); - } - - pub fn exeFileExt(self: Target) []const u8 { - return self.getTarget().exeFileExt(); - } - - pub fn staticLibSuffix(self: Target) []const u8 { - return self.getTarget().staticLibSuffix(); - } - - pub fn libPrefix(self: Target) []const u8 { - return self.getTarget().libPrefix(); - } - - pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { - return self.getTarget().zigTriple(allocator); - } - - pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { - return self.getTarget().linuxTriple(allocator); - } - - pub fn wantSharedLibSymLinks(self: Target) bool { - return self.getTarget().wantSharedLibSymLinks(); - } - - pub fn vcpkgTriplet(self: Target, allocator: *mem.Allocator, linkage: std.build.VcpkgLinkage) ![]const u8 { - return self.getTarget().vcpkgTriplet(allocator, linkage); - } - - pub fn getExternalExecutor(self: Target) std.Target.Executor { - switch (self) { - .Native => return .native, - .Cross => |t| return t.getExternalExecutor(), - } - } - - pub fn isGnuLibC(self: Target) bool { - return self.getTarget().isGnuLibC(); - } -}; +/// Deprecated. Use `std.zig.CrossTarget`. +pub const Target = std.zig.CrossTarget; pub const Pkg = struct { name: []const u8, @@ -1135,7 +1089,7 @@ pub const LibExeObjStep = struct { step: Step, builder: *Builder, name: []const u8, - target: Target, + target: CrossTarget = CrossTarget{}, linker_script: ?[]const u8 = null, version_script: ?[]const u8 = null, out_filename: []const u8, @@ -1188,7 +1142,6 @@ pub const LibExeObjStep = struct { install_step: ?*InstallArtifactStep, libc_file: ?[]const u8 = null, - target_glibc: ?Version = null, valgrind_support: ?bool = null, @@ -1288,7 +1241,6 @@ pub const LibExeObjStep = struct { .kind = kind, .root_src = root_src, .name = name, - .target = Target.Native, .frameworks = BufSet.init(builder.allocator), .step = Step.init(name, builder.allocator, make), .version = ver, @@ -1379,36 +1331,11 @@ pub const LibExeObjStep = struct { } } - /// Deprecated. Use `setTheTarget`. - pub fn setTarget( - self: *LibExeObjStep, - target_arch: builtin.Arch, - target_os: builtin.Os, - target_abi: builtin.Abi, - ) void { - return self.setTheTarget(Target{ - .Cross = CrossTarget{ - .arch = target_arch, - .os = target_os, - .abi = target_abi, - .cpu_features = target_arch.getBaselineCpuFeatures(), - }, - }); - } - - pub fn setTheTarget(self: *LibExeObjStep, target: Target) void { + pub fn setTarget(self: *LibExeObjStep, target: CrossTarget) void { self.target = target; self.computeOutFileNames(); } - pub fn setTargetGLibC(self: *LibExeObjStep, major: u32, minor: u32, patch: u32) void { - self.target_glibc = Version{ - .major = major, - .minor = minor, - .patch = patch, - }; - } - pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void { self.output_dir = self.builder.dupePath(dir); } @@ -2002,47 +1929,41 @@ pub const LibExeObjStep = struct { try zig_args.append(@tagName(self.code_model)); } - switch (self.target) { - .Native => {}, - .Cross => |cross| { - try zig_args.append("-target"); - try zig_args.append(self.target.zigTriple(builder.allocator) catch unreachable); + if (!self.target.isNative()) { + try zig_args.append("-target"); + try zig_args.append(try self.target.zigTriple(builder.allocator)); - const all_features = self.target.getArch().allFeaturesList(); - var populated_cpu_features = cross.cpu.model.features; - populated_cpu_features.populateDependencies(all_features); + // TODO this logic can disappear if cpu model + features becomes part of the target triple + const cross = self.target.toTarget(); + const all_features = cross.cpu.arch.allFeaturesList(); + var populated_cpu_features = cross.cpu.model.features; + populated_cpu_features.populateDependencies(all_features); - if (populated_cpu_features.eql(cross.cpu.features)) { - // The CPU name alone is sufficient. - // If it is the baseline CPU, no command line args are required. - if (cross.cpu.model != std.Target.Cpu.baseline(self.target.getArch()).model) { - try zig_args.append("-mcpu"); - try zig_args.append(cross.cpu.model.name); - } - } else { - var mcpu_buffer = try std.Buffer.init(builder.allocator, "-mcpu="); - try mcpu_buffer.append(cross.cpu.model.name); - - for (all_features) |feature, i_usize| { - const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); - const in_cpu_set = populated_cpu_features.isEnabled(i); - const in_actual_set = cross.cpu.features.isEnabled(i); - if (in_cpu_set and !in_actual_set) { - try mcpu_buffer.appendByte('-'); - try mcpu_buffer.append(feature.name); - } else if (!in_cpu_set and in_actual_set) { - try mcpu_buffer.appendByte('+'); - try mcpu_buffer.append(feature.name); - } - } - try zig_args.append(mcpu_buffer.toSliceConst()); + if (populated_cpu_features.eql(cross.cpu.features)) { + // The CPU name alone is sufficient. + // If it is the baseline CPU, no command line args are required. + if (cross.cpu.model != std.Target.Cpu.baseline(cross.cpu.arch).model) { + try zig_args.append("-mcpu"); + try zig_args.append(cross.cpu.model.name); } - }, - } + } else { + var mcpu_buffer = try std.Buffer.init(builder.allocator, "-mcpu="); + try mcpu_buffer.append(cross.cpu.model.name); - if (self.target_glibc) |ver| { - try zig_args.append("-target-glibc"); - try zig_args.append(builder.fmt("{}.{}.{}", .{ ver.major, ver.minor, ver.patch })); + for (all_features) |feature, i_usize| { + const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); + const in_cpu_set = populated_cpu_features.isEnabled(i); + const in_actual_set = cross.cpu.features.isEnabled(i); + if (in_cpu_set and !in_actual_set) { + try mcpu_buffer.appendByte('-'); + try mcpu_buffer.append(feature.name); + } else if (!in_cpu_set and in_actual_set) { + try mcpu_buffer.appendByte('+'); + try mcpu_buffer.append(feature.name); + } + } + try zig_args.append(mcpu_buffer.toSliceConst()); + } } if (self.linker_script) |linker_script| { @@ -2517,10 +2438,7 @@ const VcpkgRootStatus = enum { Found, }; -pub const VcpkgLinkage = enum { - Static, - Dynamic, -}; +pub const VcpkgLinkage = std.builtin.LinkMode; pub const InstallDir = enum { Prefix, diff --git a/lib/std/build/translate_c.zig b/lib/std/build/translate_c.zig index 90e95be5e1..e9e61b190f 100644 --- a/lib/std/build/translate_c.zig +++ b/lib/std/build/translate_c.zig @@ -7,6 +7,7 @@ const LibExeObjStep = build.LibExeObjStep; const CheckFileStep = build.CheckFileStep; const fs = std.fs; const mem = std.mem; +const CrossTarget = std.zig.CrossTarget; pub const TranslateCStep = struct { step: Step, @@ -14,7 +15,7 @@ pub const TranslateCStep = struct { source: build.FileSource, output_dir: ?[]const u8, out_basename: []const u8, - target: build.Target = .Native, + target: CrossTarget = CrossTarget{}, pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep { const self = builder.allocator.create(TranslateCStep) catch unreachable; @@ -39,7 +40,7 @@ pub const TranslateCStep = struct { ) catch unreachable; } - pub fn setTarget(self: *TranslateCStep, target: build.Target) void { + pub fn setTarget(self: *TranslateCStep, target: CrossTarget) void { self.target = target; } @@ -63,12 +64,9 @@ pub const TranslateCStep = struct { try argv_list.append("--cache"); try argv_list.append("on"); - switch (self.target) { - .Native => {}, - .Cross => { - try argv_list.append("-target"); - try argv_list.append(try self.target.zigTriple(self.builder.allocator)); - }, + if (!self.target.isNative()) { + try argv_list.append("-target"); + try argv_list.append(try self.target.zigTriple(self.builder.allocator)); } try argv_list.append(self.source.getPath(self.builder)); diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 3204ce905e..de37fda903 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -429,6 +429,29 @@ pub const Version = struct { .patch = try std.fmt.parseInt(u32, it.next() orelse "0", 10), }; } + + pub fn format( + self: Version, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + context: var, + comptime Error: type, + comptime output: fn (@TypeOf(context), []const u8) Error!void, + ) Error!void { + if (fmt.len == 0) { + if (self.patch == 0) { + if (self.minor == 0) { + return std.fmt.format(context, Error, output, "{}", .{self.major}); + } else { + return std.fmt.format(context, Error, output, "{}.{}", .{ self.major, self.minor }); + } + } else { + return std.fmt.format(context, Error, output, "{}.{}.{}", .{ self.major, self.minor, self.patch }); + } + } else { + @compileError("Unknown format string: '" ++ fmt ++ "'"); + } + } }; /// This data structure is used by the Zig language code generation and diff --git a/lib/std/target.zig b/lib/std/target.zig index 9007771c1a..440f50b811 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -60,6 +60,16 @@ pub const Target = struct { else => false, }; } + + pub fn dynamicLibSuffix(tag: Tag) [:0]const u8 { + if (tag.isDarwin()) { + return ".dylib"; + } + switch (tag) { + .windows => return ".dll", + else => return ".so", + } + } }; /// Based on NTDDI version constants from @@ -210,64 +220,31 @@ pub const Target = struct { } }; - pub fn parse(text: []const u8) !Os { - var it = mem.separate(text, "."); - const os_name = it.next().?; - const tag = std.meta.stringToEnum(Tag, os_name) orelse return error.UnknownOperatingSystem; - const version_text = it.rest(); - const S = struct { - fn parseNone(s: []const u8) !void { - if (s.len != 0) return error.InvalidOperatingSystemVersion; - } - fn parseSemVer(s: []const u8, d_range: Version.Range) !Version.Range { - if (s.len == 0) return d_range; - var range_it = mem.separate(s, "..."); - - const min_text = range_it.next().?; - const min_ver = Version.parse(min_text) catch |err| switch (err) { - error.Overflow => return error.InvalidOperatingSystemVersion, - error.InvalidCharacter => return error.InvalidOperatingSystemVersion, - error.InvalidVersion => return error.InvalidOperatingSystemVersion, - }; - - const max_text = range_it.next() orelse return Version.Range{ - .min = min_ver, - .max = d_range.max, - }; - const max_ver = Version.parse(max_text) catch |err| switch (err) { - error.Overflow => return error.InvalidOperatingSystemVersion, - error.InvalidCharacter => return error.InvalidOperatingSystemVersion, - error.InvalidVersion => return error.InvalidOperatingSystemVersion, - }; - - return Version.Range{ .min = min_ver, .max = max_ver }; - } - fn parseWindows(s: []const u8, d_range: WindowsVersion.Range) !WindowsVersion.Range { - if (s.len == 0) return d_range; - var range_it = mem.separate(s, "..."); - - const min_text = range_it.next().?; - const min_ver = std.meta.stringToEnum(WindowsVersion, min_text) orelse - return error.InvalidOperatingSystemVersion; - - const max_text = range_it.next() orelse return WindowsVersion.Range{ - .min = min_ver, - .max = d_range.max, - }; - const max_ver = std.meta.stringToEnum(WindowsVersion, max_text) orelse - return error.InvalidOperatingSystemVersion; - - return WindowsVersion.Range{ .min = min_ver, .max = max_ver }; - } + pub fn defaultVersionRange(tag: Tag) Os { + return .{ + .tag = tag, + .version_range = VersionRange.default(tag), }; - const d_range = VersionRange.default(tag); - switch (tag) { + } + + pub fn requiresLibC(os: Os) bool { + return switch (os.tag) { + .freebsd, + .netbsd, + .macosx, + .ios, + .tvos, + .watchos, + .dragonfly, + .openbsd, + => true, + + .linux, + .windows, .freestanding, .ananas, .cloudabi, - .dragonfly, .fuchsia, - .ios, .kfreebsd, .lv2, .solaris, @@ -282,8 +259,6 @@ pub const Target = struct { .amdhsa, .ps4, .elfiamcu, - .tvos, - .watchos, .mesa3d, .contiki, .amdpal, @@ -293,41 +268,7 @@ pub const Target = struct { .emscripten, .uefi, .other, - => return Os{ - .tag = tag, - .version_range = .{ .none = try S.parseNone(version_text) }, - }, - - .freebsd, - .macosx, - .netbsd, - .openbsd, - => return Os{ - .tag = tag, - .version_range = .{ .semver = try S.parseSemVer(version_text, d_range.semver) }, - }, - - .linux => return Os{ - .tag = tag, - .version_range = .{ - .linux = .{ - .range = try S.parseSemVer(version_text, d_range.linux.range), - .glibc = d_range.linux.glibc, - }, - }, - }, - - .windows => return Os{ - .tag = tag, - .version_range = .{ .windows = try S.parseWindows(version_text, d_range.windows) }, - }, - } - } - - pub fn defaultVersionRange(tag: Tag) Os { - return .{ - .tag = tag, - .version_range = VersionRange.default(tag), + => false, }; } }; @@ -434,6 +375,13 @@ pub const Target = struct { else => false, }; } + + pub fn oFileExt(abi: Abi) [:0]const u8 { + return switch (abi) { + .msvc => ".obj", + else => ".o", + }; + } }; pub const ObjectFormat = enum { @@ -500,6 +448,12 @@ pub const Target = struct { return Set{ .ints = [1]usize{0} ** usize_count }; } + pub fn isEmpty(set: Set) bool { + return for (set.ints) |x| { + if (x != 0) break false; + } else true; + } + pub fn isEnabled(set: Set, arch_feature_index: Index) bool { const usize_index = arch_feature_index / @bitSizeOf(usize); const bit_index = @intCast(ShiftInt, arch_feature_index % @bitSizeOf(usize)); @@ -526,6 +480,15 @@ pub const Target = struct { set.ints[usize_index] &= ~(@as(usize, 1) << bit_index); } + /// Removes the specified feature but not its dependents. + pub fn removeFeatureSet(set: *Set, other_set: Set) void { + // TODO should be able to use binary not on @Vector type. + // https://github.com/ziglang/zig/issues/903 + for (set.ints) |*int, i| { + int.* &= ~other_set.ints[i]; + } + } + pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void { @setEvalBranchQuota(1000000); @@ -663,7 +626,7 @@ pub const Target = struct { return cpu; } } - return error.UnknownCpu; + return error.UnknownCpuModel; } pub fn toElfMachine(arch: Arch) std.elf.EM { @@ -779,6 +742,66 @@ pub const Target = struct { }; } + pub fn ptrBitWidth(arch: Arch) u32 { + switch (arch) { + .avr, + .msp430, + => return 16, + + .arc, + .arm, + .armeb, + .hexagon, + .le32, + .mips, + .mipsel, + .powerpc, + .r600, + .riscv32, + .sparc, + .sparcel, + .tce, + .tcele, + .thumb, + .thumbeb, + .i386, + .xcore, + .nvptx, + .amdil, + .hsail, + .spir, + .kalimba, + .shave, + .lanai, + .wasm32, + .renderscript32, + .aarch64_32, + => return 32, + + .aarch64, + .aarch64_be, + .mips64, + .mips64el, + .powerpc64, + .powerpc64le, + .riscv64, + .x86_64, + .nvptx64, + .le64, + .amdil64, + .hsail64, + .spir64, + .wasm64, + .renderscript64, + .amdgcn, + .bpfel, + .bpfeb, + .sparcv9, + .s390x, + => return 64, + } + } + /// Returns a name that matches the lib/std/target/* directory name. pub fn genericName(arch: Arch) []const u8 { return switch (arch) { @@ -846,16 +869,6 @@ pub const Target = struct { else => &[0]*const Model{}, }; } - - pub fn parse(text: []const u8) !Arch { - const info = @typeInfo(Arch); - inline for (info.Enum.fields) |field| { - if (mem.eql(u8, text, field.name)) { - return @as(Arch, @field(Arch, field.name)); - } - } - return error.UnknownArchitecture; - } }; pub const Model = struct { @@ -872,41 +885,44 @@ pub const Target = struct { .features = features, }; } + + pub fn baseline(arch: Arch) *const Model { + const S = struct { + const generic_model = Model{ + .name = "generic", + .llvm_name = null, + .features = Cpu.Feature.Set.empty, + }; + }; + return switch (arch) { + .arm, .armeb, .thumb, .thumbeb => &arm.cpu.baseline, + .aarch64, .aarch64_be, .aarch64_32 => &aarch64.cpu.generic, + .avr => &avr.cpu.avr1, + .bpfel, .bpfeb => &bpf.cpu.generic, + .hexagon => &hexagon.cpu.generic, + .mips, .mipsel => &mips.cpu.mips32, + .mips64, .mips64el => &mips.cpu.mips64, + .msp430 => &msp430.cpu.generic, + .powerpc, .powerpc64, .powerpc64le => &powerpc.cpu.generic, + .amdgcn => &amdgpu.cpu.generic, + .riscv32 => &riscv.cpu.baseline_rv32, + .riscv64 => &riscv.cpu.baseline_rv64, + .sparc, .sparcv9, .sparcel => &sparc.cpu.generic, + .s390x => &systemz.cpu.generic, + .i386 => &x86.cpu.pentium4, + .x86_64 => &x86.cpu.x86_64, + .nvptx, .nvptx64 => &nvptx.cpu.sm_20, + .wasm32, .wasm64 => &wasm.cpu.generic, + + else => &S.generic_model, + }; + } }; /// The "default" set of CPU features for cross-compiling. A conservative set /// of features that is expected to be supported on most available hardware. pub fn baseline(arch: Arch) Cpu { - const S = struct { - const generic_model = Model{ - .name = "generic", - .llvm_name = null, - .features = Cpu.Feature.Set.empty, - }; - }; - const model = switch (arch) { - .arm, .armeb, .thumb, .thumbeb => &arm.cpu.baseline, - .aarch64, .aarch64_be, .aarch64_32 => &aarch64.cpu.generic, - .avr => &avr.cpu.avr1, - .bpfel, .bpfeb => &bpf.cpu.generic, - .hexagon => &hexagon.cpu.generic, - .mips, .mipsel => &mips.cpu.mips32, - .mips64, .mips64el => &mips.cpu.mips64, - .msp430 => &msp430.cpu.generic, - .powerpc, .powerpc64, .powerpc64le => &powerpc.cpu.generic, - .amdgcn => &amdgpu.cpu.generic, - .riscv32 => &riscv.cpu.baseline_rv32, - .riscv64 => &riscv.cpu.baseline_rv64, - .sparc, .sparcv9, .sparcel => &sparc.cpu.generic, - .s390x => &systemz.cpu.generic, - .i386 => &x86.cpu.pentium4, - .x86_64 => &x86.cpu.x86_64, - .nvptx, .nvptx64 => &nvptx.cpu.sm_20, - .wasm32, .wasm64 => &wasm.cpu.generic, - - else => &S.generic_model, - }; - return model.toCpu(arch); + return Model.baseline(arch).toCpu(arch); } }; @@ -918,239 +934,70 @@ pub const Target = struct { pub const stack_align = 16; - /// TODO add OS version ranges and glibc version - pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { - return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.cpu.arch), - @tagName(self.os.tag), - @tagName(self.abi), - }); + pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![:0]u8 { + return std.zig.CrossTarget.fromTarget(self).zigTriple(allocator); } - /// Returned slice must be freed by the caller. - pub fn vcpkgTriplet(target: Target, allocator: *mem.Allocator, linkage: std.build.VcpkgLinkage) ![]const u8 { - const arch = switch (target.cpu.arch) { - .i386 => "x86", - .x86_64 => "x64", + pub fn linuxTripleSimple(allocator: *mem.Allocator, cpu_arch: Cpu.Arch, os_tag: Os.Tag, abi: Abi) ![:0]u8 { + return std.fmt.allocPrint0(allocator, "{}-{}-{}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) }); + } - .arm, - .armeb, - .thumb, - .thumbeb, - .aarch64_32, - => "arm", + pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![:0]u8 { + return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi); + } - .aarch64, - .aarch64_be, - => "arm64", + pub fn oFileExt(self: Target) [:0]const u8 { + return self.abi.oFileExt(); + } - else => return error.VcpkgNoSuchArchitecture, - }; - - const os = switch (target.os) { - .windows => "windows", - .linux => "linux", - .macosx => "macos", - else => return error.VcpkgNoSuchOs, - }; - - if (linkage == .Static) { - return try mem.join(allocator, "-", &[_][]const u8{ arch, os, "static" }); - } else { - return try mem.join(allocator, "-", &[_][]const u8{ arch, os }); + pub fn exeFileExtSimple(cpu_arch: Cpu.Arch, os_tag: Os.Tag) [:0]const u8 { + switch (os_tag) { + .windows => return ".exe", + .uefi => return ".efi", + else => if (cpu_arch.isWasm()) { + return ".wasm"; + } else { + return ""; + }, } } - pub fn allocDescription(self: Target, allocator: *mem.Allocator) ![]u8 { - // TODO is there anything else worthy of the description that is not - // already captured in the triple? - return self.zigTriple(allocator); + pub fn exeFileExt(self: Target) [:0]const u8 { + return exeFileExtSimple(self.cpu.arch, self.os.tag); } - pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { - return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ - @tagName(self.cpu.arch), - @tagName(self.os.tag), - @tagName(self.abi), - }); - } - - pub const ParseOptions = struct { - /// This is sometimes called a "triple". It looks roughly like this: - /// riscv64-linux-gnu - /// The fields are, respectively: - /// * CPU Architecture - /// * Operating System - /// * C ABI (optional) - arch_os_abi: []const u8, - - /// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e" - /// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features - /// to remove from the set. - cpu_features: []const u8 = "baseline", - - /// If this is provided, the function will populate some information about parsing failures, - /// so that user-friendly error messages can be delivered. - diagnostics: ?*Diagnostics = null, - - pub const Diagnostics = struct { - /// If the architecture was determined, this will be populated. - arch: ?Cpu.Arch = null, - - /// If the OS was determined, this will be populated. - os: ?Os = null, - - /// If the ABI was determined, this will be populated. - abi: ?Abi = null, - - /// If the CPU name was determined, this will be populated. - cpu_name: ?[]const u8 = null, - - /// If error.UnknownCpuFeature is returned, this will be populated. - unknown_feature_name: ?[]const u8 = null, - }; - }; - - pub fn parse(args: ParseOptions) !Target { - var dummy_diags: ParseOptions.Diagnostics = undefined; - var diags = args.diagnostics orelse &dummy_diags; - - var it = mem.separate(args.arch_os_abi, "-"); - const arch_name = it.next() orelse return error.MissingArchitecture; - const arch = try Cpu.Arch.parse(arch_name); - diags.arch = arch; - - const os_name = it.next() orelse return error.MissingOperatingSystem; - var os = try Os.parse(os_name); - diags.os = os; - - const opt_abi_text = it.next(); - const abi = if (opt_abi_text) |abi_text| blk: { - var abi_it = mem.separate(abi_text, "."); - const abi = std.meta.stringToEnum(Abi, abi_it.next().?) orelse - return error.UnknownApplicationBinaryInterface; - const abi_ver_text = abi_it.rest(); - if (abi_ver_text.len != 0) { - if (os.tag == .linux and abi.isGnu()) { - os.version_range.linux.glibc = Version.parse(abi_ver_text) catch |err| switch (err) { - error.Overflow => return error.InvalidAbiVersion, - error.InvalidCharacter => return error.InvalidAbiVersion, - error.InvalidVersion => return error.InvalidAbiVersion, - }; - } else { - return error.InvalidAbiVersion; - } - } - break :blk abi; - } else Abi.default(arch, os); - diags.abi = abi; - - if (it.next() != null) return error.UnexpectedExtraField; - - const all_features = arch.allFeaturesList(); - var index: usize = 0; - while (index < args.cpu_features.len and - args.cpu_features[index] != '+' and - args.cpu_features[index] != '-') - { - index += 1; - } - const cpu_name = args.cpu_features[0..index]; - diags.cpu_name = cpu_name; - - const cpu: Cpu = if (mem.eql(u8, cpu_name, "baseline")) Cpu.baseline(arch) else blk: { - const cpu_model = try arch.parseCpuModel(cpu_name); - - var set = cpu_model.features; - while (index < args.cpu_features.len) { - const op = args.cpu_features[index]; - index += 1; - const start = index; - while (index < args.cpu_features.len and - args.cpu_features[index] != '+' and - args.cpu_features[index] != '-') - { - index += 1; - } - const feature_name = args.cpu_features[start..index]; - for (all_features) |feature, feat_index_usize| { - const feat_index = @intCast(Cpu.Feature.Set.Index, feat_index_usize); - if (mem.eql(u8, feature_name, feature.name)) { - switch (op) { - '+' => set.addFeature(feat_index), - '-' => set.removeFeature(feat_index), - else => unreachable, - } - break; - } - } else { - diags.unknown_feature_name = feature_name; - return error.UnknownCpuFeature; - } - } - set.populateDependencies(all_features); - break :blk .{ - .arch = arch, - .model = cpu_model, - .features = set, - }; - }; - return Target{ - .cpu = cpu, - .os = os, - .abi = abi, - }; - } - - pub fn oFileExt(self: Target) []const u8 { - return switch (self.abi) { - .msvc => ".obj", - else => ".o", - }; - } - - pub fn exeFileExt(self: Target) []const u8 { - if (self.os.tag == .windows) { - return ".exe"; - } else if (self.os.tag == .uefi) { - return ".efi"; - } else if (self.cpu.arch.isWasm()) { - return ".wasm"; - } else { - return ""; - } - } - - pub fn staticLibSuffix(self: Target) []const u8 { - if (self.cpu.arch.isWasm()) { + pub fn staticLibSuffix_cpu_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) [:0]const u8 { + if (cpu_arch.isWasm()) { return ".wasm"; } - switch (self.abi) { + switch (abi) { .msvc => return ".lib", else => return ".a", } } - pub fn dynamicLibSuffix(self: Target) []const u8 { - if (self.isDarwin()) { - return ".dylib"; - } - switch (self.os) { - .windows => return ".dll", - else => return ".so", - } + pub fn staticLibSuffix(self: Target) [:0]const u8 { + return staticLibSuffix_cpu_arch_abi(self.cpu.arch, self.abi); } - pub fn libPrefix(self: Target) []const u8 { - if (self.cpu.arch.isWasm()) { + pub fn dynamicLibSuffix(self: Target) [:0]const u8 { + return self.os.tag.dynamicLibSuffix(); + } + + pub fn libPrefix_cpu_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) [:0]const u8 { + if (cpu_arch.isWasm()) { return ""; } - switch (self.abi) { + switch (abi) { .msvc => return "", else => return "lib", } } + pub fn libPrefix(self: Target) [:0]const u8 { + return libPrefix_cpu_arch_abi(self.cpu.arch, self.abi); + } + pub fn getObjectFormat(self: Target) ObjectFormat { if (self.os.tag == .windows or self.os.tag == .uefi) { return .coff; @@ -1190,129 +1037,18 @@ pub const Target = struct { return self.os.tag.isDarwin(); } + pub fn isGnuLibC_os_tag_abi(os_tag: Os.Tag, abi: Abi) bool { + return os_tag == .linux and abi.isGnu(); + } + pub fn isGnuLibC(self: Target) bool { - return self.os.tag == .linux and self.abi.isGnu(); - } - - pub fn wantSharedLibSymLinks(self: Target) bool { - return self.os.tag != .windows; - } - - pub fn osRequiresLibC(self: Target) bool { - return self.isDarwin() or self.os.tag == .freebsd or self.os.tag == .netbsd; - } - - pub fn getArchPtrBitWidth(self: Target) u32 { - switch (self.cpu.arch) { - .avr, - .msp430, - => return 16, - - .arc, - .arm, - .armeb, - .hexagon, - .le32, - .mips, - .mipsel, - .powerpc, - .r600, - .riscv32, - .sparc, - .sparcel, - .tce, - .tcele, - .thumb, - .thumbeb, - .i386, - .xcore, - .nvptx, - .amdil, - .hsail, - .spir, - .kalimba, - .shave, - .lanai, - .wasm32, - .renderscript32, - .aarch64_32, - => return 32, - - .aarch64, - .aarch64_be, - .mips64, - .mips64el, - .powerpc64, - .powerpc64le, - .riscv64, - .x86_64, - .nvptx64, - .le64, - .amdil64, - .hsail64, - .spir64, - .wasm64, - .renderscript64, - .amdgcn, - .bpfel, - .bpfeb, - .sparcv9, - .s390x, - => return 64, - } + return isGnuLibC_os_tag_abi(self.os.tag, self.abi); } pub fn supportsNewStackCall(self: Target) bool { return !self.cpu.arch.isWasm(); } - pub const Executor = union(enum) { - native, - qemu: []const u8, - wine: []const u8, - wasmtime: []const u8, - unavailable, - }; - - pub fn getExternalExecutor(self: Target) Executor { - // If the target OS matches the host OS, we can use QEMU to emulate a foreign architecture. - if (self.os.tag == builtin.os.tag) { - return switch (self.cpu.arch) { - .aarch64 => Executor{ .qemu = "qemu-aarch64" }, - .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" }, - .arm => Executor{ .qemu = "qemu-arm" }, - .armeb => Executor{ .qemu = "qemu-armeb" }, - .i386 => Executor{ .qemu = "qemu-i386" }, - .mips => Executor{ .qemu = "qemu-mips" }, - .mipsel => Executor{ .qemu = "qemu-mipsel" }, - .mips64 => Executor{ .qemu = "qemu-mips64" }, - .mips64el => Executor{ .qemu = "qemu-mips64el" }, - .powerpc => Executor{ .qemu = "qemu-ppc" }, - .powerpc64 => Executor{ .qemu = "qemu-ppc64" }, - .powerpc64le => Executor{ .qemu = "qemu-ppc64le" }, - .riscv32 => Executor{ .qemu = "qemu-riscv32" }, - .riscv64 => Executor{ .qemu = "qemu-riscv64" }, - .s390x => Executor{ .qemu = "qemu-s390x" }, - .sparc => Executor{ .qemu = "qemu-sparc" }, - .x86_64 => Executor{ .qemu = "qemu-x86_64" }, - else => return .unavailable, - }; - } - - switch (self.os.tag) { - .windows => switch (self.getArchPtrBitWidth()) { - 32 => return Executor{ .wine = "wine" }, - 64 => return Executor{ .wine = "wine64" }, - else => return .unavailable, - }, - .wasi => switch (self.getArchPtrBitWidth()) { - 32 => return Executor{ .wasmtime = "wasmtime" }, - else => return .unavailable, - }, - else => return .unavailable, - } - } - pub const FloatAbi = enum { hard, soft, @@ -1359,7 +1095,7 @@ pub const Target = struct { }![:0]u8 { const a = allocator; if (self.isAndroid()) { - return mem.dupeZ(a, u8, if (self.getArchPtrBitWidth() == 64) + return mem.dupeZ(a, u8, if (self.cpu.arch.ptrBitWidth() == 64) "/system/bin/linker64" else "/system/bin/linker"); @@ -1477,52 +1213,3 @@ pub const Target = struct { } } }; - -test "Target.parse" { - { - const target = try Target.parse(.{ - .arch_os_abi = "x86_64-linux-gnu", - .cpu_features = "x86_64-sse-sse2-avx-cx8", - }); - - std.testing.expect(target.os.tag == .linux); - std.testing.expect(target.abi == .gnu); - std.testing.expect(target.cpu.arch == .x86_64); - std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse)); - std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .avx)); - std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8)); - std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov)); - std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr)); - } - { - const target = try Target.parse(.{ - .arch_os_abi = "arm-linux-musleabihf", - .cpu_features = "generic+v8a", - }); - - std.testing.expect(target.os.tag == .linux); - std.testing.expect(target.abi == .musleabihf); - std.testing.expect(target.cpu.arch == .arm); - std.testing.expect(target.cpu.model == &Target.arm.cpu.generic); - std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a)); - } - { - const target = try Target.parse(.{ - .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27", - .cpu_features = "generic+v8a", - }); - - std.testing.expect(target.cpu.arch == .aarch64); - std.testing.expect(target.os.tag == .linux); - std.testing.expect(target.os.version_range.linux.range.min.major == 3); - std.testing.expect(target.os.version_range.linux.range.min.minor == 10); - std.testing.expect(target.os.version_range.linux.range.min.patch == 0); - std.testing.expect(target.os.version_range.linux.range.max.major == 4); - std.testing.expect(target.os.version_range.linux.range.max.minor == 4); - std.testing.expect(target.os.version_range.linux.range.max.patch == 1); - std.testing.expect(target.os.version_range.linux.glibc.major == 2); - std.testing.expect(target.os.version_range.linux.glibc.minor == 27); - std.testing.expect(target.os.version_range.linux.glibc.patch == 0); - std.testing.expect(target.abi == .gnu); - } -} diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 348f651a88..398a71ff37 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -1,5 +1,3 @@ -const builtin = @import("builtin"); -const TypeId = builtin.TypeId; const std = @import("std.zig"); pub const LeakCountAllocator = @import("testing/leak_count_allocator.zig").LeakCountAllocator; @@ -65,16 +63,16 @@ pub fn expectEqual(expected: var, actual: @TypeOf(expected)) void { .Pointer => |pointer| { switch (pointer.size) { - builtin.TypeInfo.Pointer.Size.One, - builtin.TypeInfo.Pointer.Size.Many, - builtin.TypeInfo.Pointer.Size.C, + .One, + .Many, + .C, => { if (actual != expected) { std.debug.panic("expected {*}, found {*}", .{ expected, actual }); } }, - builtin.TypeInfo.Pointer.Size.Slice => { + .Slice => { if (actual.ptr != expected.ptr) { std.debug.panic("expected slice ptr {}, found {}", .{ expected.ptr, actual.ptr }); } diff --git a/lib/std/zig.zig b/lib/std/zig.zig index d76ed9dfd2..81f34b09c9 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -6,11 +6,8 @@ pub const parseStringLiteral = @import("zig/parse_string_literal.zig").parseStri pub const render = @import("zig/render.zig").render; pub const ast = @import("zig/ast.zig"); pub const system = @import("zig/system.zig"); +pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; -test "std.zig tests" { - _ = @import("zig/ast.zig"); - _ = @import("zig/parse.zig"); - _ = @import("zig/render.zig"); - _ = @import("zig/tokenizer.zig"); - _ = @import("zig/parse_string_literal.zig"); +test "" { + @import("std").meta.refAllDecls(@This()); } diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig new file mode 100644 index 0000000000..e785c40073 --- /dev/null +++ b/lib/std/zig/cross_target.zig @@ -0,0 +1,766 @@ +const std = @import("../std.zig"); +const assert = std.debug.assert; +const Target = std.Target; +const mem = std.mem; + +/// Contains all the same data as `Target`, additionally introducing the concept of "the native target". +/// The purpose of this abstraction is to provide meaningful and unsurprising defaults. +pub const CrossTarget = struct { + /// `null` means native. + cpu_arch: ?Target.Cpu.Arch = null, + + /// If `cpu_arch` is native, `null` means native. Otherwise it means baseline. + /// If this is non-null, `cpu_arch` must be specified. + cpu_model: ?*const Target.Cpu.Model = null, + + /// Sparse set of CPU features to add to the set from `cpu_model`. + /// If this is non-empty, `cpu_arch` must be specified. + cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty, + + /// Sparse set of CPU features to remove from the set from `cpu_model`. + /// If this is non-empty, `cpu_arch` must be specified. + cpu_features_sub: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty, + + /// `null` means native. + os_tag: ?Target.Os.Tag = null, + + /// `null` means the default version range for `os_tag`. If `os_tag` is `null` (native) + /// then `null` for this field means native. + os_version_min: ?OsVersion = null, + + /// When cross compiling, `null` means default (latest known OS version). + /// When `os_tag` is native, `null` means equal to the native OS version. + os_version_max: ?OsVersion = null, + + /// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI. + abi: ?Target.Abi = null, + + /// `null` means default when cross compiling, or native when os_tag is native. + /// If `isGnuLibC()` is `false`, this must be `null` and is ignored. + glibc_version: ?SemVer = null, + + pub const OsVersion = union(enum) { + none: void, + semver: SemVer, + windows: Target.Os.WindowsVersion, + }; + + pub const SemVer = std.builtin.Version; + + pub fn fromTarget(target: Target) CrossTarget { + var result: CrossTarget = .{ + .cpu_arch = target.cpu.arch, + .cpu_model = target.cpu.model, + .os_tag = target.os.tag, + .os_version_min = undefined, + .os_version_max = undefined, + .abi = target.abi, + .glibc_version = if (target.isGnuLibC()) + target.os.version_range.linux.glibc + else + null, + }; + result.updateOsVersionRange(target.os); + + const all_features = target.cpu.arch.allFeaturesList(); + var cpu_model_set = target.cpu.model.features; + cpu_model_set.populateDependencies(all_features); + { + // The "add" set is the full set with the CPU Model set removed. + const add_set = &result.cpu_features_add; + add_set.* = target.cpu.features; + add_set.removeFeatureSet(cpu_model_set); + } + { + // The "sub" set is the features that are on in CPU Model set and off in the full set. + const sub_set = &result.cpu_features_sub; + sub_set.* = cpu_model_set; + sub_set.removeFeatureSet(target.cpu.features); + } + return result; + } + + fn updateOsVersionRange(self: *CrossTarget, os: Target.Os) void { + switch (os.tag) { + .freestanding, + .ananas, + .cloudabi, + .dragonfly, + .fuchsia, + .ios, + .kfreebsd, + .lv2, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .tvos, + .watchos, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + .wasi, + .emscripten, + .uefi, + .other, + => { + self.os_version_min = .{ .none = {} }; + self.os_version_max = .{ .none = {} }; + }, + + .freebsd, + .macosx, + .netbsd, + .openbsd, + => { + self.os_version_min = .{ .semver = os.version_range.semver.min }; + self.os_version_max = .{ .semver = os.version_range.semver.max }; + }, + + .linux => { + self.os_version_min = .{ .semver = os.version_range.linux.range.min }; + self.os_version_max = .{ .semver = os.version_range.linux.range.max }; + }, + + .windows => { + self.os_version_min = .{ .windows = os.version_range.windows.min }; + self.os_version_max = .{ .windows = os.version_range.windows.max }; + }, + } + } + + pub fn toTarget(self: CrossTarget) Target { + return .{ + .cpu = self.getCpu(), + .os = self.getOs(), + .abi = self.getAbi(), + }; + } + + pub const ParseOptions = struct { + /// This is sometimes called a "triple". It looks roughly like this: + /// riscv64-linux-musl + /// The fields are, respectively: + /// * CPU Architecture + /// * Operating System (and optional version range) + /// * C ABI (optional, with optional glibc version) + /// The string "native" can be used for CPU architecture as well as Operating System. + /// If the CPU Architecture is specified as "native", then the Operating System and C ABI may be omitted. + arch_os_abi: []const u8 = "native", + + /// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e" + /// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features + /// to remove from the set. + /// The following special strings are recognized for CPU Model name: + /// * "baseline" - The "default" set of CPU features for cross-compiling. A conservative set + /// of features that is expected to be supported on most available hardware. + /// * "native" - The native CPU model is to be detected when compiling. + /// If this field is not provided (`null`), then the value will depend on the + /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline". + cpu_features: ?[]const u8 = null, + + /// If this is provided, the function will populate some information about parsing failures, + /// so that user-friendly error messages can be delivered. + diagnostics: ?*Diagnostics = null, + + pub const Diagnostics = struct { + /// If the architecture was determined, this will be populated. + arch: ?Target.Cpu.Arch = null, + + /// If the OS tag was determined, this will be populated. + os_tag: ?Target.Os.Tag = null, + + /// If the ABI was determined, this will be populated. + abi: ?Target.Abi = null, + + /// If the CPU name was determined, this will be populated. + cpu_name: ?[]const u8 = null, + + /// If error.UnknownCpuFeature is returned, this will be populated. + unknown_feature_name: ?[]const u8 = null, + }; + }; + + pub fn parse(args: ParseOptions) !CrossTarget { + var dummy_diags: ParseOptions.Diagnostics = undefined; + const diags = args.diagnostics orelse &dummy_diags; + + // Start with everything initialized to default values. + var result: CrossTarget = .{}; + + var it = mem.separate(args.arch_os_abi, "-"); + const arch_name = it.next().?; + const arch_is_native = mem.eql(u8, arch_name, "native"); + if (!arch_is_native) { + result.cpu_arch = std.meta.stringToEnum(Target.Cpu.Arch, arch_name) orelse + return error.UnknownArchitecture; + } + const arch = result.getCpuArch(); + diags.arch = arch; + + if (it.next()) |os_text| { + try parseOs(&result, diags, os_text); + } else if (!arch_is_native) { + return error.MissingOperatingSystem; + } + + const opt_abi_text = it.next(); + if (opt_abi_text) |abi_text| { + var abi_it = mem.separate(abi_text, "."); + const abi = std.meta.stringToEnum(Target.Abi, abi_it.next().?) orelse + return error.UnknownApplicationBinaryInterface; + diags.abi = abi; + + const abi_ver_text = abi_it.rest(); + if (abi_it.next() != null) { + if (result.isGnuLibC()) { + result.glibc_version = SemVer.parse(abi_ver_text) catch |err| switch (err) { + error.Overflow => return error.InvalidAbiVersion, + error.InvalidCharacter => return error.InvalidAbiVersion, + error.InvalidVersion => return error.InvalidAbiVersion, + }; + } else { + return error.InvalidAbiVersion; + } + } + } + + if (it.next() != null) return error.UnexpectedExtraField; + + if (args.cpu_features) |cpu_features| { + const all_features = arch.allFeaturesList(); + var index: usize = 0; + while (index < cpu_features.len and + cpu_features[index] != '+' and + cpu_features[index] != '-') + { + index += 1; + } + const cpu_name = cpu_features[0..index]; + diags.cpu_name = cpu_name; + + const add_set = &result.cpu_features_add; + const sub_set = &result.cpu_features_sub; + if (mem.eql(u8, cpu_name, "native")) { + result.cpu_model = null; + } else if (mem.eql(u8, cpu_name, "baseline")) { + result.cpu_model = Target.Cpu.Model.baseline(arch); + } else { + result.cpu_model = try arch.parseCpuModel(cpu_name); + } + + while (index < cpu_features.len) { + const op = cpu_features[index]; + const set = switch (op) { + '+' => add_set, + '-' => sub_set, + else => unreachable, + }; + index += 1; + const start = index; + while (index < cpu_features.len and + cpu_features[index] != '+' and + cpu_features[index] != '-') + { + index += 1; + } + const feature_name = cpu_features[start..index]; + for (all_features) |feature, feat_index_usize| { + const feat_index = @intCast(Target.Cpu.Feature.Set.Index, feat_index_usize); + if (mem.eql(u8, feature_name, feature.name)) { + set.addFeature(feat_index); + break; + } + } else { + diags.unknown_feature_name = feature_name; + return error.UnknownCpuFeature; + } + } + } + + return result; + } + + pub fn getCpu(self: CrossTarget) Target.Cpu { + if (self.cpu_arch) |arch| { + if (self.cpu_model) |model| { + var adjusted_model = model.toCpu(arch); + self.updateCpuFeatures(&adjusted_model.features); + return adjusted_model; + } else { + var adjusted_baseline = Target.Cpu.baseline(arch); + self.updateCpuFeatures(&adjusted_baseline.features); + return adjusted_baseline; + } + } else { + assert(self.cpu_model == null); + assert(self.cpu_features_sub.isEmpty()); + assert(self.cpu_features_add.isEmpty()); + // This works when doing `zig build` because Zig generates a build executable using + // native CPU model & features. However this will not be accurate otherwise, and + // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. + return Target.current.cpu; + } + } + + pub fn getCpuArch(self: CrossTarget) Target.Cpu.Arch { + return self.cpu_arch orelse Target.current.cpu.arch; + } + + pub fn getCpuModel(self: CrossTarget) *const Target.Cpu.Model { + if (self.cpu_model) |cpu_model| return cpu_model; + return self.getCpu().model; + } + + pub fn getCpuFeatures(self: CrossTarget) Target.Cpu.Feature.Set { + return self.getCpu().features; + } + + pub fn getOs(self: CrossTarget) Target.Os { + // `Target.current.os` works when doing `zig build` because Zig generates a build executable using + // native OS version range. However this will not be accurate otherwise, and + // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. + var adjusted_os = if (self.os_tag) |os_tag| Target.Os.defaultVersionRange(os_tag) else Target.current.os; + + if (self.os_version_min) |min| switch (min) { + .none => {}, + .semver => |semver| switch (self.getOsTag()) { + .linux => adjusted_os.version_range.linux.range.min = semver, + else => adjusted_os.version_range.semver.min = semver, + }, + .windows => |win_ver| adjusted_os.version_range.windows.min = win_ver, + }; + + if (self.os_version_max) |max| switch (max) { + .none => {}, + .semver => |semver| switch (self.getOsTag()) { + .linux => adjusted_os.version_range.linux.range.max = semver, + else => adjusted_os.version_range.semver.max = semver, + }, + .windows => |win_ver| adjusted_os.version_range.windows.max = win_ver, + }; + + if (self.glibc_version) |glibc| { + assert(self.isGnuLibC()); + adjusted_os.version_range.linux.glibc = glibc; + } + + return adjusted_os; + } + + pub fn getOsTag(self: CrossTarget) Target.Os.Tag { + return self.os_tag orelse Target.current.os.tag; + } + + pub fn getOsVersionMin(self: CrossTarget) OsVersion { + if (self.os_version_min) |version_min| return version_min; + var tmp: CrossTarget = undefined; + tmp.updateOsVersionRange(self.getOs()); + return tmp.os_version_min.?; + } + + pub fn getOsVersionMax(self: CrossTarget) OsVersion { + if (self.os_version_max) |version_max| return version_max; + var tmp: CrossTarget = undefined; + tmp.updateOsVersionRange(self.getOs()); + return tmp.os_version_max.?; + } + + pub fn getAbi(self: CrossTarget) Target.Abi { + if (self.abi) |abi| return abi; + + if (self.isNativeOs()) { + // This works when doing `zig build` because Zig generates a build executable using + // native CPU model & features. However this will not be accurate otherwise, and + // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. + return Target.current.abi; + } + + return Target.Abi.default(self.getCpuArch(), self.getOs()); + } + + pub fn isFreeBSD(self: CrossTarget) bool { + return self.getOsTag() == .freebsd; + } + + pub fn isDarwin(self: CrossTarget) bool { + return self.getOsTag().isDarwin(); + } + + pub fn isNetBSD(self: CrossTarget) bool { + return self.getOsTag() == .netbsd; + } + + pub fn isUefi(self: CrossTarget) bool { + return self.getOsTag() == .uefi; + } + + pub fn isDragonFlyBSD(self: CrossTarget) bool { + return self.getOsTag() == .dragonfly; + } + + pub fn isLinux(self: CrossTarget) bool { + return self.getOsTag() == .linux; + } + + pub fn isWindows(self: CrossTarget) bool { + return self.getOsTag() == .windows; + } + + pub fn oFileExt(self: CrossTarget) [:0]const u8 { + return self.getAbi().oFileExt(); + } + + pub fn exeFileExt(self: CrossTarget) [:0]const u8 { + return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag()); + } + + pub fn staticLibSuffix(self: CrossTarget) [:0]const u8 { + return Target.staticLibSuffix_cpu_arch_abi(self.getCpuArch(), self.getAbi()); + } + + pub fn dynamicLibSuffix(self: CrossTarget) [:0]const u8 { + return self.getOsTag().dynamicLibSuffix(); + } + + pub fn libPrefix(self: CrossTarget) [:0]const u8 { + return Target.libPrefix_cpu_arch_abi(self.getCpuArch(), self.getAbi()); + } + + pub fn isNativeCpu(self: CrossTarget) bool { + return self.cpu_arch == null and self.cpu_model == null and + self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty(); + } + + pub fn isNativeOs(self: CrossTarget) bool { + return self.os_tag == null and self.os_version_min == null and self.os_version_max == null; + } + + pub fn isNativeAbi(self: CrossTarget) bool { + return self.abi == null and self.glibc_version == null; + } + + pub fn isNative(self: CrossTarget) bool { + return self.isNativeCpu() and self.isNativeOs() and self.isNativeAbi(); + } + + pub fn zigTriple(self: CrossTarget, allocator: *mem.Allocator) error{OutOfMemory}![:0]u8 { + if (self.isNative()) { + return mem.dupeZ(allocator, u8, "native"); + } + + const arch_name = if (self.isNativeCpu()) "native" else @tagName(self.getCpuArch()); + const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native"; + + var result = try std.Buffer.allocPrint(allocator, "{}-{}", .{ arch_name, os_name }); + defer result.deinit(); + + // The zig target syntax does not allow specifying a max os version with no min, so + // if either are present, we need the min. + if (self.os_version_min != null or self.os_version_max != null) { + switch (self.getOsVersionMin()) { + .none => {}, + .semver => |v| try result.print(".{}", .{v}), + .windows => |v| try result.print(".{}", .{@tagName(v)}), + } + } + if (self.os_version_max) |max| { + switch (max) { + .none => {}, + .semver => |v| try result.print("...{}", .{v}), + .windows => |v| try result.print("...{}", .{@tagName(v)}), + } + } + + if (self.abi) |abi| { + try result.print("-{}", .{@tagName(abi)}); + if (self.glibc_version) |v| { + try result.print(".{}", .{v}); + } + } else { + assert(self.glibc_version == null); + } + + return result.toOwnedSlice(); + } + + pub fn allocDescription(self: CrossTarget, allocator: *mem.Allocator) ![:0]u8 { + // TODO is there anything else worthy of the description that is not + // already captured in the triple? + return self.zigTriple(allocator); + } + + pub fn linuxTriple(self: CrossTarget, allocator: *mem.Allocator) ![:0]u8 { + return Target.linuxTripleSimple(allocator, self.getCpuArch(), self.getOsTag(), self.getAbi()); + } + + pub fn wantSharedLibSymLinks(self: CrossTarget) bool { + return self.getOsTag() != .windows; + } + + pub const VcpkgLinkage = std.builtin.LinkMode; + + /// Returned slice must be freed by the caller. + pub fn vcpkgTriplet(self: CrossTarget, allocator: *mem.Allocator, linkage: VcpkgLinkage) ![:0]u8 { + const arch = switch (self.getCpuArch()) { + .i386 => "x86", + .x86_64 => "x64", + + .arm, + .armeb, + .thumb, + .thumbeb, + .aarch64_32, + => "arm", + + .aarch64, + .aarch64_be, + => "arm64", + + else => return error.UnsupportedVcpkgArchitecture, + }; + + const os = switch (self.getOsTag()) { + .windows => "windows", + .linux => "linux", + .macosx => "macos", + else => return error.UnsupportedVcpkgOperatingSystem, + }; + + const static_suffix = switch (linkage) { + .Static => "-static", + .Dynamic => "", + }; + + return std.fmt.allocPrint0(allocator, "{}-{}{}", .{ arch, os, static_suffix }); + } + + pub const Executor = union(enum) { + native, + qemu: []const u8, + wine: []const u8, + wasmtime: []const u8, + unavailable, + }; + + pub fn getExternalExecutor(self: CrossTarget) Executor { + const os_tag = self.getOsTag(); + const cpu_arch = self.getCpuArch(); + + // If the target OS matches the host OS, we can use QEMU to emulate a foreign architecture. + if (os_tag == Target.current.os.tag) { + return switch (cpu_arch) { + .aarch64 => Executor{ .qemu = "qemu-aarch64" }, + .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" }, + .arm => Executor{ .qemu = "qemu-arm" }, + .armeb => Executor{ .qemu = "qemu-armeb" }, + .i386 => Executor{ .qemu = "qemu-i386" }, + .mips => Executor{ .qemu = "qemu-mips" }, + .mipsel => Executor{ .qemu = "qemu-mipsel" }, + .mips64 => Executor{ .qemu = "qemu-mips64" }, + .mips64el => Executor{ .qemu = "qemu-mips64el" }, + .powerpc => Executor{ .qemu = "qemu-ppc" }, + .powerpc64 => Executor{ .qemu = "qemu-ppc64" }, + .powerpc64le => Executor{ .qemu = "qemu-ppc64le" }, + .riscv32 => Executor{ .qemu = "qemu-riscv32" }, + .riscv64 => Executor{ .qemu = "qemu-riscv64" }, + .s390x => Executor{ .qemu = "qemu-s390x" }, + .sparc => Executor{ .qemu = "qemu-sparc" }, + .x86_64 => Executor{ .qemu = "qemu-x86_64" }, + else => return .unavailable, + }; + } + + switch (os_tag) { + .windows => switch (cpu_arch.ptrBitWidth()) { + 32 => return Executor{ .wine = "wine" }, + 64 => return Executor{ .wine = "wine64" }, + else => return .unavailable, + }, + .wasi => switch (cpu_arch.ptrBitWidth()) { + 32 => return Executor{ .wasmtime = "wasmtime" }, + else => return .unavailable, + }, + else => return .unavailable, + } + } + + pub fn isGnuLibC(self: CrossTarget) bool { + return Target.isGnuLibC_os_tag_abi(self.getOsTag(), self.getAbi()); + } + + pub fn setGnuLibCVersion(self: CrossTarget, major: u32, minor: u32, patch: u32) void { + assert(self.isGnuLibC()); + self.glibc_version = SemVer{ .major = major, .minor = minor, .patch = patch }; + } + + fn updateCpuFeatures(self: CrossTarget, set: *Target.Cpu.Feature.Set) void { + set.removeFeatureSet(self.cpu_features_sub); + set.addFeatureSet(self.cpu_features_add); + set.populateDependencies(self.getCpuArch().allFeaturesList()); + set.removeFeatureSet(self.cpu_features_sub); + } + + fn parseOs(result: *CrossTarget, diags: *ParseOptions.Diagnostics, text: []const u8) !void { + var it = mem.separate(text, "."); + const os_name = it.next().?; + const os_is_native = mem.eql(u8, os_name, "native"); + if (!os_is_native) { + result.os_tag = std.meta.stringToEnum(Target.Os.Tag, os_name) orelse + return error.UnknownOperatingSystem; + } + const tag = result.getOsTag(); + diags.os_tag = tag; + + const version_text = it.rest(); + if (it.next() == null) return; + + switch (tag) { + .freestanding, + .ananas, + .cloudabi, + .dragonfly, + .fuchsia, + .ios, + .kfreebsd, + .lv2, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .tvos, + .watchos, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + .wasi, + .emscripten, + .uefi, + .other, + => return error.InvalidOperatingSystemVersion, + + .freebsd, + .macosx, + .netbsd, + .openbsd, + .linux, + => { + var range_it = mem.separate(version_text, "..."); + + const min_text = range_it.next().?; + const min_ver = SemVer.parse(min_text) catch |err| switch (err) { + error.Overflow => return error.InvalidOperatingSystemVersion, + error.InvalidCharacter => return error.InvalidOperatingSystemVersion, + error.InvalidVersion => return error.InvalidOperatingSystemVersion, + }; + result.os_version_min = .{ .semver = min_ver }; + + const max_text = range_it.next() orelse return; + const max_ver = SemVer.parse(max_text) catch |err| switch (err) { + error.Overflow => return error.InvalidOperatingSystemVersion, + error.InvalidCharacter => return error.InvalidOperatingSystemVersion, + error.InvalidVersion => return error.InvalidOperatingSystemVersion, + }; + result.os_version_max = .{ .semver = max_ver }; + }, + + .windows => { + var range_it = mem.separate(version_text, "..."); + + const min_text = range_it.next().?; + const min_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, min_text) orelse + return error.InvalidOperatingSystemVersion; + result.os_version_min = .{ .windows = min_ver }; + + const max_text = range_it.next() orelse return; + const max_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, max_text) orelse + return error.InvalidOperatingSystemVersion; + result.os_version_max = .{ .windows = max_ver }; + }, + } + } +}; + +test "CrossTarget.parse" { + { + const cross_target = try CrossTarget.parse(.{ + .arch_os_abi = "x86_64-linux-gnu", + .cpu_features = "x86_64-sse-sse2-avx-cx8", + }); + const target = cross_target.toTarget(); + + std.testing.expect(target.os.tag == .linux); + std.testing.expect(target.abi == .gnu); + std.testing.expect(target.cpu.arch == .x86_64); + std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse)); + std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .avx)); + std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8)); + std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov)); + std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr)); + + const text = try cross_target.zigTriple(std.testing.allocator); + defer std.testing.allocator.free(text); + std.testing.expectEqualSlices(u8, "x86_64-linux-gnu", text); + } + { + const cross_target = try CrossTarget.parse(.{ + .arch_os_abi = "arm-linux-musleabihf", + .cpu_features = "generic+v8a", + }); + const target = cross_target.toTarget(); + + std.testing.expect(target.os.tag == .linux); + std.testing.expect(target.abi == .musleabihf); + std.testing.expect(target.cpu.arch == .arm); + std.testing.expect(target.cpu.model == &Target.arm.cpu.generic); + std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a)); + + const text = try cross_target.zigTriple(std.testing.allocator); + defer std.testing.allocator.free(text); + std.testing.expectEqualSlices(u8, "arm-linux-musleabihf", text); + } + { + const cross_target = try CrossTarget.parse(.{ + .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27", + .cpu_features = "generic+v8a", + }); + const target = cross_target.toTarget(); + + std.testing.expect(target.cpu.arch == .aarch64); + std.testing.expect(target.os.tag == .linux); + std.testing.expect(target.os.version_range.linux.range.min.major == 3); + std.testing.expect(target.os.version_range.linux.range.min.minor == 10); + std.testing.expect(target.os.version_range.linux.range.min.patch == 0); + std.testing.expect(target.os.version_range.linux.range.max.major == 4); + std.testing.expect(target.os.version_range.linux.range.max.minor == 4); + std.testing.expect(target.os.version_range.linux.range.max.patch == 1); + std.testing.expect(target.os.version_range.linux.glibc.major == 2); + std.testing.expect(target.os.version_range.linux.glibc.minor == 27); + std.testing.expect(target.os.version_range.linux.glibc.patch == 0); + std.testing.expect(target.abi == .gnu); + + const text = try cross_target.zigTriple(std.testing.allocator); + defer std.testing.allocator.free(text); + std.testing.expectEqualSlices(u8, "aarch64-linux.3.10...4.4.1-gnu.2.27", text); + } +} diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 7f0f35abe0..ecaad0daf2 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -10,6 +10,7 @@ const Allocator = mem.Allocator; const ArrayList = std.ArrayList; const Buffer = std.Buffer; const Target = std.Target; +const CrossTarget = std.zig.CrossTarget; const self_hosted_main = @import("main.zig"); const errmsg = @import("errmsg.zig"); const DepTokenizer = @import("dep_tokenizer.zig").Tokenizer; @@ -87,7 +88,7 @@ const Error = extern enum { NotLazy, IsAsync, ImportOutsidePkgPath, - UnknownCpu, + UnknownCpuModel, UnknownCpuFeature, InvalidCpuFeatures, InvalidLlvmCpuFeaturesFormat, @@ -634,13 +635,9 @@ export fn stage2_cmd_targets(zig_triple: [*:0]const u8) c_int { } fn cmdTargets(zig_triple: [*:0]const u8) !void { - var target = try Target.parse(.{ .arch_os_abi = mem.toSliceConst(u8, zig_triple) }); - target.cpu = blk: { - const llvm = @import("llvm.zig"); - const llvm_cpu_name = llvm.GetHostCPUName(); - const llvm_cpu_features = llvm.GetNativeFeatures(); - break :blk try detectNativeCpuWithLLVM(target.cpu.arch, llvm_cpu_name, llvm_cpu_features); - }; + var cross_target = try CrossTarget.parse(.{ .arch_os_abi = mem.toSliceConst(u8, zig_triple) }); + var dynamic_linker: ?[*:0]u8 = null; + const target = try crossTargetToTarget(cross_target, &dynamic_linker); return @import("print_targets.zig").cmdTargets( std.heap.c_allocator, &[0][]u8{}, @@ -661,7 +658,6 @@ export fn stage2_target_parse( error.UnknownOperatingSystem => return .UnknownOperatingSystem, error.UnknownApplicationBinaryInterface => return .UnknownApplicationBinaryInterface, error.MissingOperatingSystem => return .MissingOperatingSystem, - error.MissingArchitecture => return .MissingArchitecture, error.InvalidLlvmCpuFeaturesFormat => return .InvalidLlvmCpuFeaturesFormat, error.UnexpectedExtraField => return .SemanticAnalyzeFail, error.InvalidAbiVersion => return .InvalidAbiVersion, @@ -681,44 +677,42 @@ fn stage2TargetParse( zig_triple_oz: ?[*:0]const u8, mcpu_oz: ?[*:0]const u8, ) !void { - const target: std.build.Target = if (zig_triple_oz) |zig_triple_z| blk: { + const target: CrossTarget = if (zig_triple_oz) |zig_triple_z| blk: { const zig_triple = mem.toSliceConst(u8, zig_triple_z); const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else "baseline"; - var diags: std.Target.ParseOptions.Diagnostics = .{}; - break :blk std.build.Target{ - .Cross = Target.parse(.{ - .arch_os_abi = zig_triple, - .cpu_features = mcpu, - .diagnostics = &diags, - }) catch |err| switch (err) { - error.UnknownCpu => { - std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{ - diags.cpu_name.?, - @tagName(diags.arch.?), - }); - for (diags.arch.?.allCpuModels()) |cpu| { - std.debug.warn(" {}\n", .{cpu.name}); - } - process.exit(1); - }, - error.UnknownCpuFeature => { - std.debug.warn( - \\Unknown CPU feature: '{}' - \\Available CPU features for architecture '{}': - \\ - , .{ - diags.unknown_feature_name, - @tagName(diags.arch.?), - }); - for (diags.arch.?.allFeaturesList()) |feature| { - std.debug.warn(" {}: {}\n", .{ feature.name, feature.description }); - } - process.exit(1); - }, - else => |e| return e, + var diags: CrossTarget.ParseOptions.Diagnostics = .{}; + break :blk CrossTarget.parse(.{ + .arch_os_abi = zig_triple, + .cpu_features = mcpu, + .diagnostics = &diags, + }) catch |err| switch (err) { + error.UnknownCpuModel => { + std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{ + diags.cpu_name.?, + @tagName(diags.arch.?), + }); + for (diags.arch.?.allCpuModels()) |cpu| { + std.debug.warn(" {}\n", .{cpu.name}); + } + process.exit(1); }, + error.UnknownCpuFeature => { + std.debug.warn( + \\Unknown CPU feature: '{}' + \\Available CPU features for architecture '{}': + \\ + , .{ + diags.unknown_feature_name, + @tagName(diags.arch.?), + }); + for (diags.arch.?.allFeaturesList()) |feature| { + std.debug.warn(" {}: {}\n", .{ feature.name, feature.description }); + } + process.exit(1); + }, + else => |e| return e, }; - } else std.build.Target.Native; + } else .{}; try stage1_target.fromTarget(target); } @@ -908,8 +902,8 @@ const Stage2Target = extern struct { dynamic_linker: ?[*:0]const u8, - fn toTarget(in_target: Stage2Target) std.build.Target { - if (in_target.is_native) return .Native; + fn toTarget(in_target: Stage2Target) CrossTarget { + if (in_target.is_native) return .{}; const in_arch = in_target.arch - 1; // skip over ZigLLVM_UnknownArch const in_os = in_target.os; @@ -924,28 +918,11 @@ const Stage2Target = extern struct { }; } - fn fromTarget(self: *Stage2Target, build_target: std.build.Target) !void { + fn fromTarget(self: *Stage2Target, cross_target: CrossTarget) !void { const allocator = std.heap.c_allocator; - var dynamic_linker: ?[*:0]u8 = null; - const target = switch (build_target) { - .Native => blk: { - const info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator); - if (info.dynamic_linker) |dl| { - dynamic_linker = dl.ptr; - } - // TODO we want to just use info.target but implementing CPU model & feature detection is todo - // so here we rely on LLVM - const llvm = @import("llvm.zig"); - const llvm_cpu_name = llvm.GetHostCPUName(); - const llvm_cpu_features = llvm.GetNativeFeatures(); - const arch = std.Target.current.cpu.arch; - var t = info.target; - t.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); - break :blk t; - }, - .Cross => |t| t, - }; + var dynamic_linker: ?[*:0]u8 = null; + const target = try crossTargetToTarget(cross_target, &dynamic_linker); var cache_hash = try std.Buffer.allocPrint(allocator, "{}\n{}\n", .{ target.cpu.model.name, @@ -1145,7 +1122,7 @@ const Stage2Target = extern struct { .cpu_builtin_str = cpu_builtin_str_buffer.toOwnedSlice().ptr, .os_builtin_str = os_builtin_str_buffer.toOwnedSlice().ptr, .cache_hash = cache_hash.toOwnedSlice().ptr, - .is_native = build_target == .Native, + .is_native = cross_target.isNative(), .glibc_version = glibc_version, .dynamic_linker = dynamic_linker, }; @@ -1156,6 +1133,40 @@ fn enumInt(comptime Enum: type, int: c_int) Enum { return @intToEnum(Enum, @intCast(@TagType(Enum), int)); } +/// TODO move dynamic linker to be part of the target +/// TODO self-host this function +fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target { + var adjusted_target = cross_target.toTarget(); + if (cross_target.isNativeCpu() or cross_target.isNativeOs()) { + const detected_info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator); + if (cross_target.isNativeCpu()) { + adjusted_target.cpu = detected_info.target.cpu; + + // TODO We want to just use detected_info.target but implementing + // CPU model & feature detection is todo so here we rely on LLVM. + // There is another occurrence of this; search for detectNativeCpuWithLLVM. + const llvm = @import("llvm.zig"); + const llvm_cpu_name = llvm.GetHostCPUName(); + const llvm_cpu_features = llvm.GetNativeFeatures(); + const arch = std.Target.current.cpu.arch; + adjusted_target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); + } + if (cross_target.isNativeOs()) { + adjusted_target.os = detected_info.target.os; + + if (detected_info.dynamic_linker) |dl| { + dynamic_linker_ptr.* = dl.ptr; + } + if (cross_target.abi == null) { + adjusted_target.abi = detected_info.target.abi; + } + } else if (cross_target.abi == null) { + adjusted_target.abi = Target.Abi.default(adjusted_target.cpu.arch, adjusted_target.os); + } + } + return adjusted_target; +} + // ABI warning const Stage2GLibCVersion = extern struct { major: u32, diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 7c1eb6d409..979bf45bbe 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,5 +1,5 @@ const tests = @import("tests.zig"); -const Target = @import("std").Target; +const std = @import("std"); pub fn addCases(cases: *tests.CompileErrorContext) void { cases.addTest("type mismatch with tuple concatenation", @@ -386,12 +386,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , &[_][]const u8{ "tmp.zig:3:5: error: target arch 'wasm32' does not support calling with a new stack", }); - tc.target = tests.Target{ - .Cross = .{ - .cpu = Target.Cpu.baseline(.wasm32), - .os = Target.Os.defaultVersionRange(.wasi), - .abi = .none, - }, + tc.target = std.zig.CrossTarget{ + .cpu_arch = .wasm32, + .os_tag = .wasi, + .abi = .none, }; break :x tc; }); @@ -787,12 +785,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , &[_][]const u8{ "tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs", }); - tc.target = tests.Target{ - .Cross = .{ - .cpu = Target.Cpu.baseline(.x86_64), - .os = Target.Os.defaultVersionRange(.linux), - .abi = .gnu, - }, + tc.target = std.zig.CrossTarget{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .gnu, }; break :x tc; }); @@ -1452,7 +1448,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:2:18: error: invalid operands to binary expression: 'error{A}' and 'error{B}'", }); - if (Target.current.os.tag == .linux) { + if (std.Target.current.os.tag == .linux) { cases.addTest("implicit dependency on libc", \\extern "c" fn exit(u8) void; \\export fn entry() void { diff --git a/test/src/translate_c.zig b/test/src/translate_c.zig index 968f09eeb8..9a6bd0d323 100644 --- a/test/src/translate_c.zig +++ b/test/src/translate_c.zig @@ -7,6 +7,7 @@ const fmt = std.fmt; const mem = std.mem; const fs = std.fs; const warn = std.debug.warn; +const CrossTarget = std.zig.CrossTarget; pub const TranslateCContext = struct { b: *build.Builder, @@ -19,7 +20,7 @@ pub const TranslateCContext = struct { sources: ArrayList(SourceFile), expected_lines: ArrayList([]const u8), allow_warnings: bool, - target: build.Target = .Native, + target: CrossTarget = CrossTarget{}, const SourceFile = struct { filename: []const u8, @@ -75,7 +76,7 @@ pub const TranslateCContext = struct { pub fn addWithTarget( self: *TranslateCContext, name: []const u8, - target: build.Target, + target: CrossTarget, source: []const u8, expected_lines: []const []const u8, ) void { diff --git a/test/tests.zig b/test/tests.zig index 78eaf56273..9cf4e7bd98 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -3,7 +3,7 @@ const builtin = std.builtin; const debug = std.debug; const warn = debug.warn; const build = std.build; -pub const Target = build.Target; +const CrossTarget = std.zig.CrossTarget; const Buffer = std.Buffer; const io = std.io; const fs = std.fs; @@ -30,7 +30,7 @@ pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTransla pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext; const TestTarget = struct { - target: build.Target = .Native, + target: CrossTarget = @as(CrossTarget, .{}), mode: builtin.Mode = .Debug, link_libc: bool = false, single_threaded: bool = false, @@ -52,105 +52,85 @@ const test_targets = blk: { }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.x86_64), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .none, - }, + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .none, }, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.x86_64), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .gnu, - }, + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .gnu, }, .link_libc = true, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.x86_64), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .musl, - }, - }, - .link_libc = true, - }, - - TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.i386), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .none, - }, - }, - }, - TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.i386), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .musl, - }, - }, - .link_libc = true, - }, - - TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.aarch64), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .none, - }, - }, - }, - TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.aarch64), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .musl, - }, - }, - .link_libc = true, - }, - TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.aarch64), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .gnu, - }, + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .musl, }, .link_libc = true, }, TestTarget{ .target = .{ - .Cross = std.Target.parse(.{ - .arch_os_abi = "arm-linux-none", - .cpu_features = "generic+v8a", - }) catch unreachable, + .cpu_arch = .i386, + .os_tag = .linux, + .abi = .none, }, }, TestTarget{ .target = .{ - .Cross = std.Target.parse(.{ - .arch_os_abi = "arm-linux-musleabihf", - .cpu_features = "generic+v8a", - }) catch unreachable, + .cpu_arch = .i386, + .os_tag = .linux, + .abi = .musl, }, .link_libc = true, }, + + TestTarget{ + .target = .{ + .cpu_arch = .aarch64, + .os_tag = .linux, + .abi = .none, + }, + }, + TestTarget{ + .target = .{ + .cpu_arch = .aarch64, + .os_tag = .linux, + .abi = .musl, + }, + .link_libc = true, + }, + TestTarget{ + .target = .{ + .cpu_arch = .aarch64, + .os_tag = .linux, + .abi = .gnu, + }, + .link_libc = true, + }, + + TestTarget{ + .target = CrossTarget.parse(.{ + .arch_os_abi = "arm-linux-none", + .cpu_features = "generic+v8a", + }) catch unreachable, + }, + TestTarget{ + .target = CrossTarget.parse(.{ + .arch_os_abi = "arm-linux-musleabihf", + .cpu_features = "generic+v8a", + }) catch unreachable, + .link_libc = true, + }, // TODO https://github.com/ziglang/zig/issues/3287 //TestTarget{ - // .target = std.Target.parse(.{ + // .target = CrossTarget.parse(.{ // .arch_os_abi = "arm-linux-gnueabihf", // .cpu_features = "generic+v8a", // }) catch unreachable, @@ -158,75 +138,61 @@ const test_targets = blk: { //}, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.mipsel), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .none, - }, + .target = .{ + .cpu_arch = .mipsel, + .os_tag = .linux, + .abi = .none, }, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.mipsel), - .os = std.Target.Os.defaultVersionRange(.linux), - .abi = .musl, - }, + .target = .{ + .cpu_arch = .mipsel, + .os_tag = .linux, + .abi = .musl, }, .link_libc = true, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.x86_64), - .os = std.Target.Os.defaultVersionRange(.macosx), - .abi = .gnu, - }, + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .macosx, + .abi = .gnu, }, // TODO https://github.com/ziglang/zig/issues/3295 .disable_native = true, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.i386), - .os = std.Target.Os.defaultVersionRange(.windows), - .abi = .msvc, - }, + .target = .{ + .cpu_arch = .i386, + .os_tag = .windows, + .abi = .msvc, }, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.x86_64), - .os = std.Target.Os.defaultVersionRange(.windows), - .abi = .msvc, - }, + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .windows, + .abi = .msvc, }, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.i386), - .os = std.Target.Os.defaultVersionRange(.windows), - .abi = .gnu, - }, + .target = .{ + .cpu_arch = .i386, + .os_tag = .windows, + .abi = .gnu, }, .link_libc = true, }, TestTarget{ - .target = Target{ - .Cross = .{ - .cpu = std.Target.Cpu.baseline(.x86_64), - .os = std.Target.Os.defaultVersionRange(.windows), - .abi = .gnu, - }, + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .windows, + .abi = .gnu, }, .link_libc = true, }, @@ -435,13 +401,13 @@ pub fn addPkgTests( const step = b.step(b.fmt("test-{}", .{name}), desc); for (test_targets) |test_target| { - if (skip_non_native and test_target.target != .Native) + if (skip_non_native and !test_target.target.isNative()) continue; if (skip_libc and test_target.link_libc) continue; - if (test_target.link_libc and test_target.target.getTarget().osRequiresLibC()) { + if (test_target.link_libc and test_target.target.getOs().requiresLibC()) { // This would be a redundant test. continue; } @@ -451,8 +417,8 @@ pub fn addPkgTests( const ArchTag = @TagType(builtin.Arch); if (test_target.disable_native and - test_target.target.getOs() == std.Target.current.os.tag and - test_target.target.getArch() == std.Target.current.cpu.arch) + test_target.target.getOsTag() == std.Target.current.os.tag and + test_target.target.getCpuArch() == std.Target.current.cpu.arch) { continue; } @@ -462,17 +428,14 @@ pub fn addPkgTests( } else false; if (!want_this_mode) continue; - const libc_prefix = if (test_target.target.getTarget().osRequiresLibC()) + const libc_prefix = if (test_target.target.getOs().requiresLibC()) "" else if (test_target.link_libc) "c" else "bare"; - const triple_prefix = if (test_target.target == .Native) - @as([]const u8, "native") - else - test_target.target.zigTriple(b.allocator) catch unreachable; + const triple_prefix = test_target.target.zigTriple(b.allocator) catch unreachable; const these_tests = b.addTest(root_src); const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; @@ -486,7 +449,7 @@ pub fn addPkgTests( these_tests.single_threaded = test_target.single_threaded; these_tests.setFilter(test_filter); these_tests.setBuildMode(test_target.mode); - these_tests.setTheTarget(test_target.target); + these_tests.setTarget(test_target.target); if (test_target.link_libc) { these_tests.linkSystemLibrary("c"); } @@ -716,7 +679,7 @@ pub const CompileErrorContext = struct { link_libc: bool, is_exe: bool, is_test: bool, - target: Target = .Native, + target: CrossTarget = CrossTarget{}, const SourceFile = struct { filename: []const u8, @@ -808,12 +771,9 @@ pub const CompileErrorContext = struct { zig_args.append("--output-dir") catch unreachable; zig_args.append(b.pathFromRoot(b.cache_root)) catch unreachable; - switch (self.case.target) { - .Native => {}, - .Cross => { - try zig_args.append("-target"); - try zig_args.append(try self.case.target.zigTriple(b.allocator)); - }, + if (!self.case.target.isNative()) { + try zig_args.append("-target"); + try zig_args.append(try self.case.target.zigTriple(b.allocator)); } switch (self.build_mode) { diff --git a/test/translate_c.zig b/test/translate_c.zig index 07364fb032..1d6c2a60ae 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,6 @@ const tests = @import("tests.zig"); const std = @import("std"); -const Target = std.Target; +const CrossTarget = std.zig.CrossTarget; pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("macro line continuation", @@ -665,7 +665,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - if (Target.current.os.tag != .windows) { + if (std.Target.current.os.tag != .windows) { // Windows treats this as an enum with type c_int cases.add("big negative enum init values when C ABI supports long long enums", \\enum EnumWithInits { @@ -1064,7 +1064,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - if (Target.current.os.tag != .windows) { + if (std.Target.current.os.tag != .windows) { // sysv_abi not currently supported on windows cases.add("Macro qualified functions", \\void __attribute__((sysv_abi)) foo(void); @@ -1094,11 +1094,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { }); cases.addWithTarget("Calling convention", .{ - .Cross = .{ - .cpu = Target.Cpu.baseline(.i386), - .os = Target.Os.defaultVersionRange(.linux), - .abi = .none, - }, + .cpu_arch = .i386, + .os_tag = .linux, + .abi = .none, }, \\void __attribute__((fastcall)) foo1(float *a); \\void __attribute__((stdcall)) foo2(float *a); @@ -1113,12 +1111,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub fn foo5(a: [*c]f32) callconv(.Thiscall) void; }); - cases.addWithTarget("Calling convention", .{ - .Cross = Target.parse(.{ - .arch_os_abi = "arm-linux-none", - .cpu_features = "generic+v8_5a", - }) catch unreachable, - }, + cases.addWithTarget("Calling convention", CrossTarget.parse(.{ + .arch_os_abi = "arm-linux-none", + .cpu_features = "generic+v8_5a", + }) catch unreachable, \\void __attribute__((pcs("aapcs"))) foo1(float *a); \\void __attribute__((pcs("aapcs-vfp"))) foo2(float *a); , &[_][]const u8{ @@ -1126,12 +1122,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub fn foo2(a: [*c]f32) callconv(.AAPCSVFP) void; }); - cases.addWithTarget("Calling convention", .{ - .Cross = Target.parse(.{ - .arch_os_abi = "aarch64-linux-none", - .cpu_features = "generic+v8_5a", - }) catch unreachable, - }, + cases.addWithTarget("Calling convention", CrossTarget.parse(.{ + .arch_os_abi = "aarch64-linux-none", + .cpu_features = "generic+v8_5a", + }) catch unreachable, \\void __attribute__((aarch64_vector_pcs)) foo1(float *a); , &[_][]const u8{ \\pub fn foo1(a: [*c]f32) callconv(.Vectorcall) void; @@ -1600,7 +1594,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - if (Target.current.os.tag != .windows) { + if (std.Target.current.os.tag != .windows) { // When clang uses the -windows-none triple it behaves as MSVC and // interprets the inner `struct Bar` as an anonymous structure cases.add("type referenced struct", From c8669a4cf834a6d1dadd9260e94f1781ceed0ec3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 14:33:10 -0500 Subject: [PATCH 42/99] improve debug info for optionals --- src/analyze.cpp | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index ceb232c79d..2c8244d053 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -8719,7 +8719,6 @@ static void resolve_llvm_types_optional(CodeGen *g, ZigType *type, ResolveStatus if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return; } - LLVMTypeRef child_llvm_type = get_llvm_type(g, child_type); ZigLLVMDIType *child_llvm_di_type = get_llvm_di_type(g, child_type); if (type->data.maybe.resolve_status >= wanted_resolve_status) return; @@ -8729,35 +8728,28 @@ static void resolve_llvm_types_optional(CodeGen *g, ZigType *type, ResolveStatus }; LLVMStructSetBody(type->llvm_type, elem_types, 2, false); - uint64_t val_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_llvm_type); - uint64_t val_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_llvm_type); - uint64_t val_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 0); + uint64_t val_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, maybe_child_index); + uint64_t maybe_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, maybe_null_index); - uint64_t maybe_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, bool_llvm_type); - uint64_t maybe_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, bool_llvm_type); - uint64_t maybe_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 1); - - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, type->llvm_type); - uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, type->llvm_type); - - ZigLLVMDIType *di_element_types[] = { + ZigLLVMDIType *di_element_types[2]; + di_element_types[maybe_child_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type), "val", di_file, line, - val_debug_size_in_bits, - val_debug_align_in_bits, + 8 * child_type->abi_size, + 8 * child_type->abi_align, val_offset_in_bits, - ZigLLVM_DIFlags_Zero, child_llvm_di_type), + ZigLLVM_DIFlags_Zero, child_llvm_di_type); + di_element_types[maybe_null_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type), "maybe", di_file, line, - maybe_debug_size_in_bits, - maybe_debug_align_in_bits, + 8*g->builtin_types.entry_bool->abi_size, + 8*g->builtin_types.entry_bool->abi_align, maybe_offset_in_bits, - ZigLLVM_DIFlags_Zero, bool_llvm_di_type), - }; + ZigLLVM_DIFlags_Zero, bool_llvm_di_type); ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, compile_unit_scope, buf_ptr(&type->name), - di_file, line, debug_size_in_bits, debug_align_in_bits, ZigLLVM_DIFlags_Zero, + di_file, line, 8 * type->abi_size, 8 * type->abi_align, ZigLLVM_DIFlags_Zero, nullptr, di_element_types, 2, 0, nullptr, ""); ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type); From cf233bad5884b9bd782256eb6808fcc6abda645c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 14:33:31 -0500 Subject: [PATCH 43/99] fix target parsing --- lib/std/zig/cross_target.zig | 15 +++++++++++++++ src-self-hosted/stage2.zig | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index e785c40073..6654635b89 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -177,6 +177,9 @@ pub const CrossTarget = struct { /// If the architecture was determined, this will be populated. arch: ?Target.Cpu.Arch = null, + /// If the OS name was determined, this will be populated. + os_name: ?[]const u8 = null, + /// If the OS tag was determined, this will be populated. os_tag: ?Target.Os.Tag = null, @@ -219,6 +222,7 @@ pub const CrossTarget = struct { var abi_it = mem.separate(abi_text, "."); const abi = std.meta.stringToEnum(Target.Abi, abi_it.next().?) orelse return error.UnknownApplicationBinaryInterface; + result.abi = abi; diags.abi = abi; const abi_ver_text = abi_it.rest(); @@ -614,6 +618,7 @@ pub const CrossTarget = struct { fn parseOs(result: *CrossTarget, diags: *ParseOptions.Diagnostics, text: []const u8) !void { var it = mem.separate(text, "."); const os_name = it.next().?; + diags.os_name = os_name; const os_is_native = mem.eql(u8, os_name, "native"); if (!os_is_native) { result.os_tag = std.meta.stringToEnum(Target.Os.Tag, os_name) orelse @@ -702,6 +707,16 @@ pub const CrossTarget = struct { }; test "CrossTarget.parse" { + { + const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "native" }); + + std.testing.expect(cross_target.cpu_arch == null); + std.testing.expect(cross_target.isNative()); + + const text = try cross_target.zigTriple(std.testing.allocator); + defer std.testing.allocator.free(text); + std.testing.expectEqualSlices(u8, "native", text); + } { const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "x86_64-linux-gnu", diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index ecaad0daf2..b2963c8a71 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -679,7 +679,7 @@ fn stage2TargetParse( ) !void { const target: CrossTarget = if (zig_triple_oz) |zig_triple_z| blk: { const zig_triple = mem.toSliceConst(u8, zig_triple_z); - const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else "baseline"; + const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else null; var diags: CrossTarget.ParseOptions.Diagnostics = .{}; break :blk CrossTarget.parse(.{ .arch_os_abi = zig_triple, From cebcacd872a05d8ee3edaafe3eff5cc6e73657b7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 14:33:36 -0500 Subject: [PATCH 44/99] fix standardTargetOptions and improve init-exe to use it --- lib/std/build.zig | 24 +++++++++++++++++++----- lib/std/special/init-exe/build.zig | 10 ++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 92b06a0261..792781df85 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -525,7 +525,7 @@ pub const Builder = struct { pub const StandardTargetOptionsArgs = struct { whitelist: ?[]const CrossTarget = null, - default_target: CrossTarget = .{}, + default_target: CrossTarget = CrossTarget{}, }; /// Exposes standard `zig build` options for choosing a target. @@ -533,12 +533,12 @@ pub const Builder = struct { const triple = self.option( []const u8, "target", - "The Arch, OS, and ABI to build for.", + "The CPU architecture, OS, and ABI to build for.", ) orelse return args.default_target; // TODO add cpu and features as part of the target triple - var diags: std.Target.ParseOptions.Diagnostics = .{}; + var diags: CrossTarget.ParseOptions.Diagnostics = .{}; const selected_target = CrossTarget.parse(.{ .arch_os_abi = triple, .diagnostics = &diags, @@ -567,7 +567,21 @@ pub const Builder = struct { } process.exit(1); }, - else => |e| return e, + error.UnknownOperatingSystem => { + std.debug.warn( + \\Unknown OS: '{}' + \\Available operating systems: + \\ + , .{diags.os_name}); + inline for (std.meta.fields(std.Target.Os.Tag)) |field| { + std.debug.warn(" {}\n", .{field.name}); + } + process.exit(1); + }, + else => |e| { + std.debug.warn("Unable to parse target '{}': {}\n", .{ triple, @errorName(e) }); + process.exit(1); + }, }; const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable; @@ -585,7 +599,7 @@ pub const Builder = struct { }); for (list) |t| { const t_triple = t.zigTriple(self.allocator) catch unreachable; - std.debug.warn(" {}\n", t_triple); + std.debug.warn(" {}\n", .{t_triple}); } // TODO instead of process exit, return error and have a zig build flag implemented by // the build runner that turns process exits into error return traces diff --git a/lib/std/special/init-exe/build.zig b/lib/std/special/init-exe/build.zig index 0b7410f2ad..fd71588c5f 100644 --- a/lib/std/special/init-exe/build.zig +++ b/lib/std/special/init-exe/build.zig @@ -1,8 +1,18 @@ const Builder = @import("std").build.Builder; pub fn build(b: *Builder) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard release options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. const mode = b.standardReleaseOptions(); + const exe = b.addExecutable("$", "src/main.zig"); + exe.setTarget(target); exe.setBuildMode(mode); exe.install(); From 0912484c4f1a8b278a4f04b3baa76cfa38daef6e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 15:35:48 -0500 Subject: [PATCH 45/99] improve the "external executor" detection logic --- lib/std/build.zig | 3 +-- lib/std/zig/cross_target.zig | 36 +++++++++++++++++------------------- src-self-hosted/stage2.zig | 6 +++--- test/stack_traces.zig | 2 +- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 792781df85..23db4c3f8e 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1141,7 +1141,7 @@ pub const LibExeObjStep = struct { out_pdb_filename: []const u8, packages: ArrayList(Pkg), build_options_contents: std.Buffer, - system_linker_hack: bool, + system_linker_hack: bool = false, object_src: []const u8, @@ -1273,7 +1273,6 @@ pub const LibExeObjStep = struct { .object_src = undefined, .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable, .c_std = Builder.CStd.C99, - .system_linker_hack = false, .override_lib_dir = null, .main_pkg_path = null, .exec_cmd_args = null, diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 6654635b89..1e16072815 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -383,7 +383,7 @@ pub const CrossTarget = struct { pub fn getAbi(self: CrossTarget) Target.Abi { if (self.abi) |abi| return abi; - if (self.isNativeOs()) { + if (self.os_tag == null) { // This works when doing `zig build` because Zig generates a build executable using // native CPU model & features. However this will not be accurate otherwise, and // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. @@ -441,21 +441,11 @@ pub const CrossTarget = struct { return Target.libPrefix_cpu_arch_abi(self.getCpuArch(), self.getAbi()); } - pub fn isNativeCpu(self: CrossTarget) bool { - return self.cpu_arch == null and self.cpu_model == null and - self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty(); - } - - pub fn isNativeOs(self: CrossTarget) bool { - return self.os_tag == null and self.os_version_min == null and self.os_version_max == null; - } - - pub fn isNativeAbi(self: CrossTarget) bool { - return self.abi == null and self.glibc_version == null; - } - pub fn isNative(self: CrossTarget) bool { - return self.isNativeCpu() and self.isNativeOs() and self.isNativeAbi(); + return self.cpu_arch == null and self.cpu_model == null and + self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty() and + self.os_tag == null and self.os_version_min == null and self.os_version_max == null and + self.abi == null; } pub fn zigTriple(self: CrossTarget, allocator: *mem.Allocator) error{OutOfMemory}![:0]u8 { @@ -463,7 +453,7 @@ pub const CrossTarget = struct { return mem.dupeZ(allocator, u8, "native"); } - const arch_name = if (self.isNativeCpu()) "native" else @tagName(self.getCpuArch()); + const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native"; const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native"; var result = try std.Buffer.allocPrint(allocator, "{}-{}", .{ arch_name, os_name }); @@ -557,12 +547,20 @@ pub const CrossTarget = struct { unavailable, }; + /// Note that even a `CrossTarget` which returns `false` for `isNative` could still be natively executed. + /// For example `-target arm-native` running on an aarch64 host. pub fn getExternalExecutor(self: CrossTarget) Executor { - const os_tag = self.getOsTag(); const cpu_arch = self.getCpuArch(); + const os_tag = self.getOsTag(); + const os_match = os_tag == Target.current.os.tag; - // If the target OS matches the host OS, we can use QEMU to emulate a foreign architecture. - if (os_tag == Target.current.os.tag) { + // If the OS matches, and the CPU arch matches, the binary is considered native. + if (self.os_tag == null and cpu_arch == Target.current.cpu.arch) { + return .native; + } + + // If the OS matches, we can use QEMU to emulate a foreign architecture. + if (os_match) { return switch (cpu_arch) { .aarch64 => Executor{ .qemu = "qemu-aarch64" }, .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" }, diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index b2963c8a71..351c6c5357 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1137,9 +1137,9 @@ fn enumInt(comptime Enum: type, int: c_int) Enum { /// TODO self-host this function fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target { var adjusted_target = cross_target.toTarget(); - if (cross_target.isNativeCpu() or cross_target.isNativeOs()) { + if (cross_target.cpu_arch == null or cross_target.os_tag == null) { const detected_info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator); - if (cross_target.isNativeCpu()) { + if (cross_target.cpu_arch == null) { adjusted_target.cpu = detected_info.target.cpu; // TODO We want to just use detected_info.target but implementing @@ -1151,7 +1151,7 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) const arch = std.Target.current.cpu.arch; adjusted_target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); } - if (cross_target.isNativeOs()) { + if (cross_target.os_tag == null) { adjusted_target.os = detected_info.target.os; if (detected_info.dynamic_linker) |dl| { diff --git a/test/stack_traces.zig b/test/stack_traces.zig index ab1156c3cb..b16bf485c0 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -42,7 +42,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\} ; - switch (builtin.os.tag) { + switch (std.Target.current.os.tag) { .freebsd => { cases.addCase( "return", From 622b5b62c293b767787786e753cecff40f29d7e7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 16:32:28 -0500 Subject: [PATCH 46/99] fix not setting the dynamic linker path when cross compiling --- src-self-hosted/stage2.zig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 351c6c5357..61068f1737 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -668,6 +668,7 @@ export fn stage2_target_parse( error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded, error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded, error.DeviceBusy => return .DeviceBusy, + error.UnknownDynamicLinkerPath => return .UnknownDynamicLinkerPath, }; return .None; } @@ -1137,6 +1138,7 @@ fn enumInt(comptime Enum: type, int: c_int) Enum { /// TODO self-host this function fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target { var adjusted_target = cross_target.toTarget(); + var have_native_dl = false; if (cross_target.cpu_arch == null or cross_target.os_tag == null) { const detected_info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator); if (cross_target.cpu_arch == null) { @@ -1155,6 +1157,7 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) adjusted_target.os = detected_info.target.os; if (detected_info.dynamic_linker) |dl| { + have_native_dl = true; dynamic_linker_ptr.* = dl.ptr; } if (cross_target.abi == null) { @@ -1164,6 +1167,14 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) adjusted_target.abi = Target.Abi.default(adjusted_target.cpu.arch, adjusted_target.os); } } + if (!have_native_dl) { + dynamic_linker_ptr.* = adjusted_target.getStandardDynamicLinkerPath( + std.heap.c_allocator, + ) catch |err| switch (err) { + error.TargetHasNoDynamicLinker => null, + else => |e| return e, + }; + } return adjusted_target; } From 34d2700af42d8f45caaa168b34c34b33bb14cf8d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 17:35:43 -0500 Subject: [PATCH 47/99] clean up CrossTarget.getExternalExecutor --- lib/std/zig/cross_target.zig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 1e16072815..fac90ddba8 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -5,6 +5,7 @@ const mem = std.mem; /// Contains all the same data as `Target`, additionally introducing the concept of "the native target". /// The purpose of this abstraction is to provide meaningful and unsurprising defaults. +/// This struct does reference any resources and it is copyable. pub const CrossTarget = struct { /// `null` means native. cpu_arch: ?Target.Cpu.Arch = null, @@ -554,9 +555,13 @@ pub const CrossTarget = struct { const os_tag = self.getOsTag(); const os_match = os_tag == Target.current.os.tag; - // If the OS matches, and the CPU arch matches, the binary is considered native. - if (self.os_tag == null and cpu_arch == Target.current.cpu.arch) { - return .native; + // If the OS and CPU arch match, the binary can be considered native. + if (os_match and cpu_arch == Target.current.cpu.arch) { + // However, we also need to verify that the dynamic linker path is valid. + // TODO Until that is implemented, we prevent returning `.native` when the OS is non-native. + if (self.os_tag == null) { + return .native; + } } // If the OS matches, we can use QEMU to emulate a foreign architecture. From 7927764cc21592e19591095105c397736f060edf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 18:56:43 -0500 Subject: [PATCH 48/99] mips: implement Target.getStandardDynamicLinkerPath --- lib/std/target.zig | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index 440f50b811..110aa2dcff 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1151,7 +1151,16 @@ pub const Target = struct { .mipsel, .mips64, .mips64el, - => return error.UnknownDynamicLinkerPath, + => { + const lib_suffix = switch (self.abi) { + .gnuabin32, .gnux32 => "32", + .gnuabi64 => "64", + else => "", + }; + const is_nan_2008 = mips.featureSetHas(self.cpu.features, .nan2008); + const loader = if (is_nan_2008) "ld-linux-mipsn8.so.1" else "ld.so.1"; + return std.fmt.allocPrint0(a, "/lib{}/{}", .{ lib_suffix, loader }); + }, .powerpc => return mem.dupeZ(a, u8, "/lib/ld.so.1"), .powerpc64, .powerpc64le => return mem.dupeZ(a, u8, "/lib64/ld64.so.2"), From 2387f48d5c5a5ba1410e29098a0cdbf03c3bd9a9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 20:16:14 -0500 Subject: [PATCH 49/99] fix incorrect builtin import code for windows --- src-self-hosted/stage2.zig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 61068f1737..e7265ef6d3 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1029,7 +1029,7 @@ const Stage2Target = extern struct { .netbsd, .openbsd, => try os_builtin_str_buffer.print( - \\.semver = .{{ + \\ .semver = .{{ \\ .min = .{{ \\ .major = {}, \\ .minor = {}, @@ -1041,6 +1041,7 @@ const Stage2Target = extern struct { \\ .patch = {}, \\ }}, \\ }}}}, + \\ , .{ target.os.version_range.semver.min.major, target.os.version_range.semver.min.minor, @@ -1052,7 +1053,7 @@ const Stage2Target = extern struct { }), .linux => try os_builtin_str_buffer.print( - \\.linux = .{{ + \\ .linux = .{{ \\ .range = .{{ \\ .min = .{{ \\ .major = {}, @@ -1087,10 +1088,11 @@ const Stage2Target = extern struct { }), .windows => try os_builtin_str_buffer.print( - \\.semver = .{{ + \\ .windows = .{{ \\ .min = .{}, \\ .max = .{}, \\ }}}}, + \\ , .{ @tagName(target.os.version_range.windows.min), @tagName(target.os.version_range.windows.max), From 6226726571cf6fd500fbc3735e604c85abae641c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 21:29:31 -0500 Subject: [PATCH 50/99] fix builder.findProgram test --- lib/std/build.zig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 23db4c3f8e..7852828f7c 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1039,9 +1039,10 @@ pub const Builder = struct { }; test "builder.findProgram compiles" { - var buf: [50000]u8 = undefined; - var fba = std.heap.FixedBufferAllocator.init(&buf); - const builder = try Builder.create(&fba.allocator, "zig", "zig-cache", "zig-cache"); + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + + const builder = try Builder.create(&arena.allocator, "zig", "zig-cache", "zig-cache"); defer builder.destroy(); _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; } From 36aa3c8e7ff1c945c9014bcd5431a4eedd217e8c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 11:52:55 -0500 Subject: [PATCH 51/99] fix __stack_chk_guard emitted even when not linking libc --- lib/std/special/compiler_rt.zig | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 3126e81b9d..9ed866f62d 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -2,10 +2,7 @@ const std = @import("std"); const builtin = std.builtin; const is_test = builtin.is_test; -const is_gnu = switch (builtin.abi) { - .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, - else => false, -}; +const is_gnu = std.Target.current.abi.isGnu(); const is_mingw = builtin.os.tag == .windows and is_gnu; comptime { @@ -289,7 +286,7 @@ comptime { else => {}, } } else { - if (std.Target.current.isGnuLibC()) { + if (std.Target.current.isGnuLibC() and builtin.link_libc) { @export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = linkage }); } @export(@import("compiler_rt/divti3.zig").__divti3, .{ .name = "__divti3", .linkage = linkage }); From bafa895561a6603351be8d5c3521c9a447645d08 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 12:19:16 -0500 Subject: [PATCH 52/99] compiler-rt: inline at call site to workaround a bug The bug is #2154 --- lib/std/special/compiler_rt/truncXfYf2.zig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/std/special/compiler_rt/truncXfYf2.zig b/lib/std/special/compiler_rt/truncXfYf2.zig index 7c83c66ec0..cba5b85264 100644 --- a/lib/std/special/compiler_rt/truncXfYf2.zig +++ b/lib/std/special/compiler_rt/truncXfYf2.zig @@ -1,23 +1,23 @@ const std = @import("std"); pub fn __truncsfhf2(a: f32) callconv(.C) u16 { - return @bitCast(u16, truncXfYf2(f16, f32, a)); + return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f32, a })); } pub fn __truncdfhf2(a: f64) callconv(.C) u16 { - return @bitCast(u16, truncXfYf2(f16, f64, a)); + return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f64, a })); } pub fn __trunctfsf2(a: f128) callconv(.C) f32 { - return truncXfYf2(f32, f128, a); + return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f128, a }); } pub fn __trunctfdf2(a: f128) callconv(.C) f64 { - return truncXfYf2(f64, f128, a); + return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f64, f128, a }); } pub fn __truncdfsf2(a: f64) callconv(.C) f32 { - return truncXfYf2(f32, f64, a); + return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f64, a }); } pub fn __aeabi_d2f(a: f64) callconv(.AAPCS) f32 { @@ -35,7 +35,7 @@ pub fn __aeabi_f2h(a: f32) callconv(.AAPCS) u16 { return @call(.{ .modifier = .always_inline }, __truncsfhf2, .{a}); } -inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { +fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { const src_rep_t = std.meta.IntType(false, @typeInfo(src_t).Float.bits); const dst_rep_t = std.meta.IntType(false, @typeInfo(dst_t).Float.bits); const srcSigBits = std.math.floatMantissaBits(src_t); From 2536e4c70c6805b096992d4e54dfd2ab9f7fcae3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 13:31:59 -0500 Subject: [PATCH 53/99] WASI has no dynamic linker --- lib/std/target.zig | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index 110aa2dcff..9cd65e7661 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1207,6 +1207,31 @@ pub const Target = struct { => return error.UnknownDynamicLinkerPath, }, + .ananas, + .cloudabi, + .fuchsia, + .kfreebsd, + .lv2, + .openbsd, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + => return error.UnknownDynamicLinkerPath, + .freestanding, .ios, .tvos, @@ -1216,9 +1241,8 @@ pub const Target = struct { .windows, .emscripten, .other, + .wasi, => return error.TargetHasNoDynamicLinker, - - else => return error.UnknownDynamicLinkerPath, } } }; From 3c3316d4ba3ede6172fc0a92b6c4c9b8e601adde Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 13:32:35 -0500 Subject: [PATCH 54/99] update tests to new Target API --- test/cli.zig | 3 +-- test/compare_output.zig | 9 ++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/test/cli.zig b/test/cli.zig index b7d03d9e21..bc5a29be44 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const builtin = @import("builtin"); const testing = std.testing; const process = std.process; const fs = std.fs; @@ -97,7 +96,7 @@ fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { } fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { - if (builtin.os != .linux or builtin.arch != .x86_64) return; + if (std.Target.current.os.tag != .linux or std.Target.current.cpu.arch != .x86_64) return; const example_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.zig" }); const example_s_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.s" }); diff --git a/test/compare_output.zig b/test/compare_output.zig index 7a41d46f54..ec89af35f8 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -1,4 +1,3 @@ -const builtin = @import("builtin"); const std = @import("std"); const os = std.os; const tests = @import("tests.zig"); @@ -131,8 +130,8 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { , "Hello, world!\n 12 12 a\n"); cases.addC("number literals", - \\const builtin = @import("builtin"); - \\const is_windows = builtin.os == builtin.Os.windows; + \\const std = @import("std"); + \\const is_windows = std.Target.current.os.tag == .windows; \\const c = @cImport({ \\ if (is_windows) { \\ // See https://github.com/ziglang/zig/issues/515 @@ -306,8 +305,8 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { , ""); cases.addC("casting between float and integer types", - \\const builtin = @import("builtin"); - \\const is_windows = builtin.os == builtin.Os.windows; + \\const std = @import("std"); + \\const is_windows = std.Target.current.os.tag == .windows; \\const c = @cImport({ \\ if (is_windows) { \\ // See https://github.com/ziglang/zig/issues/515 From 70bf8874d7b358545c3370d5e1ae71dfcdf55b93 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 13:32:49 -0500 Subject: [PATCH 55/99] update docgen to new Target API --- doc/docgen.zig | 81 +++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/doc/docgen.zig b/doc/docgen.zig index 9b8aca18d0..83d1a66179 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -1,5 +1,5 @@ -const builtin = @import("builtin"); const std = @import("std"); +const builtin = std.builtin; const io = std.io; const fs = std.fs; const process = std.process; @@ -521,7 +521,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {}", .{code_kind_str}); } - var mode = builtin.Mode.Debug; + var mode: builtin.Mode = .Debug; var link_objects = std.ArrayList([]const u8).init(allocator); defer link_objects.deinit(); var target_str: ?[]const u8 = null; @@ -533,9 +533,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { const end_code_tag = try eatToken(tokenizer, Token.Id.TagContent); const end_tag_name = tokenizer.buffer[end_code_tag.start..end_code_tag.end]; if (mem.eql(u8, end_tag_name, "code_release_fast")) { - mode = builtin.Mode.ReleaseFast; + mode = .ReleaseFast; } else if (mem.eql(u8, end_tag_name, "code_release_safe")) { - mode = builtin.Mode.ReleaseSafe; + mode = .ReleaseSafe; } else if (mem.eql(u8, end_tag_name, "code_link_object")) { _ = try eatToken(tokenizer, Token.Id.Separator); const obj_tok = try eatToken(tokenizer, Token.Id.TagContent); @@ -1001,30 +1001,30 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var for (toc.nodes) |node| { switch (node) { - Node.Content => |data| { + .Content => |data| { try out.write(data); }, - Node.Link => |info| { + .Link => |info| { if (!toc.urls.contains(info.url)) { return parseError(tokenizer, info.token, "url not found: {}", .{info.url}); } try out.print("{}", .{ info.url, info.name }); }, - Node.Nav => { + .Nav => { try out.write(toc.toc); }, - Node.Builtin => |tok| { + .Builtin => |tok| { try out.write("
");
                 try tokenizeAndPrintRaw(tokenizer, out, tok, builtin_code);
                 try out.write("
"); }, - Node.HeaderOpen => |info| { + .HeaderOpen => |info| { try out.print( "{} §\n", .{ info.n, info.url, info.url, info.name, info.url, info.n }, ); }, - Node.SeeAlso => |items| { + .SeeAlso => |items| { try out.write("

See also:

    \n"); for (items) |item| { const url = try urlize(allocator, item.name); @@ -1035,10 +1035,10 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var } try out.write("
\n"); }, - Node.Syntax => |content_tok| { + .Syntax => |content_tok| { try tokenizeAndPrint(tokenizer, out, content_tok); }, - Node.Code => |code| { + .Code => |code| { code_progress_index += 1; warn("docgen example code {}/{}...", .{ code_progress_index, tokenizer.code_node_count }); @@ -1075,16 +1075,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var }); try out.print("
$ zig build-exe {}.zig", .{code.name});
                         switch (code.mode) {
-                            builtin.Mode.Debug => {},
-                            builtin.Mode.ReleaseSafe => {
+                            .Debug => {},
+                            .ReleaseSafe => {
                                 try build_args.append("--release-safe");
                                 try out.print(" --release-safe", .{});
                             },
-                            builtin.Mode.ReleaseFast => {
+                            .ReleaseFast => {
                                 try build_args.append("--release-fast");
                                 try out.print(" --release-fast", .{});
                             },
-                            builtin.Mode.ReleaseSmall => {
+                            .ReleaseSmall => {
                                 try build_args.append("--release-small");
                                 try out.print(" --release-small", .{});
                             },
@@ -1142,13 +1142,14 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
                             try out.print("\n{}
\n", .{colored_stderr}); break :code_block; } - const exec_result = exec(allocator, &env_map, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile", .{}); + const exec_result = exec(allocator, &env_map, build_args.toSliceConst()) catch + return parseError(tokenizer, code.source_token, "example failed to compile", .{}); if (code.target_str) |triple| { - if (mem.startsWith(u8, triple, "wasm32") or + if ((mem.startsWith(u8, triple, "wasm32") or mem.startsWith(u8, triple, "riscv64-linux") or - mem.startsWith(u8, triple, "x86_64-linux") and - (builtin.os != .linux or builtin.arch != .x86_64)) + mem.startsWith(u8, triple, "x86_64-linux")) and + (std.Target.current.os.tag != .linux or std.Target.current.cpu.arch != .x86_64)) { // skip execution try out.print("\n", .{}); @@ -1207,16 +1208,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var }); try out.print("
$ zig test {}.zig", .{code.name});
                         switch (code.mode) {
-                            builtin.Mode.Debug => {},
-                            builtin.Mode.ReleaseSafe => {
+                            .Debug => {},
+                            .ReleaseSafe => {
                                 try test_args.append("--release-safe");
                                 try out.print(" --release-safe", .{});
                             },
-                            builtin.Mode.ReleaseFast => {
+                            .ReleaseFast => {
                                 try test_args.append("--release-fast");
                                 try out.print(" --release-fast", .{});
                             },
-                            builtin.Mode.ReleaseSmall => {
+                            .ReleaseSmall => {
                                 try test_args.append("--release-small");
                                 try out.print(" --release-small", .{});
                             },
@@ -1249,16 +1250,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
                         });
                         try out.print("
$ zig test {}.zig", .{code.name});
                         switch (code.mode) {
-                            builtin.Mode.Debug => {},
-                            builtin.Mode.ReleaseSafe => {
+                            .Debug => {},
+                            .ReleaseSafe => {
                                 try test_args.append("--release-safe");
                                 try out.print(" --release-safe", .{});
                             },
-                            builtin.Mode.ReleaseFast => {
+                            .ReleaseFast => {
                                 try test_args.append("--release-fast");
                                 try out.print(" --release-fast", .{});
                             },
-                            builtin.Mode.ReleaseSmall => {
+                            .ReleaseSmall => {
                                 try test_args.append("--release-small");
                                 try out.print(" --release-small", .{});
                             },
@@ -1306,16 +1307,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
                         });
                         var mode_arg: []const u8 = "";
                         switch (code.mode) {
-                            builtin.Mode.Debug => {},
-                            builtin.Mode.ReleaseSafe => {
+                            .Debug => {},
+                            .ReleaseSafe => {
                                 try test_args.append("--release-safe");
                                 mode_arg = " --release-safe";
                             },
-                            builtin.Mode.ReleaseFast => {
+                            .ReleaseFast => {
                                 try test_args.append("--release-fast");
                                 mode_arg = " --release-fast";
                             },
-                            builtin.Mode.ReleaseSmall => {
+                            .ReleaseSmall => {
                                 try test_args.append("--release-small");
                                 mode_arg = " --release-small";
                             },
@@ -1386,20 +1387,20 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
                         }
 
                         switch (code.mode) {
-                            builtin.Mode.Debug => {},
-                            builtin.Mode.ReleaseSafe => {
+                            .Debug => {},
+                            .ReleaseSafe => {
                                 try build_args.append("--release-safe");
                                 if (!code.is_inline) {
                                     try out.print(" --release-safe", .{});
                                 }
                             },
-                            builtin.Mode.ReleaseFast => {
+                            .ReleaseFast => {
                                 try build_args.append("--release-fast");
                                 if (!code.is_inline) {
                                     try out.print(" --release-fast", .{});
                                 }
                             },
-                            builtin.Mode.ReleaseSmall => {
+                            .ReleaseSmall => {
                                 try build_args.append("--release-small");
                                 if (!code.is_inline) {
                                     try out.print(" --release-small", .{});
@@ -1461,16 +1462,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
                         });
                         try out.print("
$ zig build-lib {}.zig", .{code.name});
                         switch (code.mode) {
-                            builtin.Mode.Debug => {},
-                            builtin.Mode.ReleaseSafe => {
+                            .Debug => {},
+                            .ReleaseSafe => {
                                 try test_args.append("--release-safe");
                                 try out.print(" --release-safe", .{});
                             },
-                            builtin.Mode.ReleaseFast => {
+                            .ReleaseFast => {
                                 try test_args.append("--release-fast");
                                 try out.print(" --release-fast", .{});
                             },
-                            builtin.Mode.ReleaseSmall => {
+                            .ReleaseSmall => {
                                 try test_args.append("--release-small");
                                 try out.print(" --release-small", .{});
                             },

From f89a1844bfc97f2023fc9961cdf12900ccb77cf8 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Thu, 27 Feb 2020 13:33:01 -0500
Subject: [PATCH 56/99] don't error out for targets with unknown standard
 dynamic linker path

---
 src-self-hosted/stage2.zig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig
index e7265ef6d3..14673b0e98 100644
--- a/src-self-hosted/stage2.zig
+++ b/src-self-hosted/stage2.zig
@@ -668,7 +668,6 @@ export fn stage2_target_parse(
         error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded,
         error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded,
         error.DeviceBusy => return .DeviceBusy,
-        error.UnknownDynamicLinkerPath => return .UnknownDynamicLinkerPath,
     };
     return .None;
 }
@@ -1174,6 +1173,7 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8)
             std.heap.c_allocator,
         ) catch |err| switch (err) {
             error.TargetHasNoDynamicLinker => null,
+            error.UnknownDynamicLinkerPath => null,
             else => |e| return e,
         };
     }

From 662b5f7c6045ba857d0d9abd72350a82f0b7ddf3 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Thu, 27 Feb 2020 14:41:44 -0500
Subject: [PATCH 57/99] update docs to latest Target API

---
 doc/docgen.zig      |  6 +++---
 doc/langref.html.in | 30 +++++++++++++++---------------
 2 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/doc/docgen.zig b/doc/docgen.zig
index 83d1a66179..5d7f2b7b38 100644
--- a/doc/docgen.zig
+++ b/doc/docgen.zig
@@ -1146,10 +1146,10 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
                             return parseError(tokenizer, code.source_token, "example failed to compile", .{});
 
                         if (code.target_str) |triple| {
-                            if ((mem.startsWith(u8, triple, "wasm32") or
+                            if (mem.startsWith(u8, triple, "wasm32") or
                                 mem.startsWith(u8, triple, "riscv64-linux") or
-                                mem.startsWith(u8, triple, "x86_64-linux")) and
-                                (std.Target.current.os.tag != .linux or std.Target.current.cpu.arch != .x86_64))
+                                (mem.startsWith(u8, triple, "x86_64-linux") and
+                                std.Target.current.os.tag != .linux or std.Target.current.cpu.arch != .x86_64))
                             {
                                 // skip execution
                                 try out.print("
\n", .{}); diff --git a/doc/langref.html.in b/doc/langref.html.in index e244d69e25..c10fc3ed30 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -965,7 +965,8 @@ const nan = std.math.nan(f128); but you can switch to {#syntax#}Optimized{#endsyntax#} mode on a per-block basis:

{#code_begin|obj|foo#} {#code_release_fast#} -const builtin = @import("builtin"); +const std = @import("std"); +const builtin = std.builtin; const big = @as(f64, 1 << 40); export fn foo_strict(x: f64) f64 { @@ -2063,15 +2064,15 @@ test "pointer child type" { alignment of the underlying type, it can be omitted from the type:

{#code_begin|test#} -const assert = @import("std").debug.assert; -const builtin = @import("builtin"); +const std = @import("std"); +const assert = std.debug.assert; test "variable alignment" { var x: i32 = 1234; const align_of_i32 = @alignOf(@TypeOf(x)); assert(@TypeOf(&x) == *i32); assert(*i32 == *align(align_of_i32) i32); - if (builtin.arch == builtin.Arch.x86_64) { + if (std.Target.current.cpu.arch == .x86_64) { assert((*i32).alignment == 4); } } @@ -2474,7 +2475,7 @@ test "default struct initialization fields" {

{#code_begin|test#} const std = @import("std"); -const builtin = @import("builtin"); +const builtin = std.builtin; const assert = std.debug.assert; const Full = packed struct { @@ -3204,8 +3205,8 @@ test "separate scopes" { {#header_open|switch#} {#code_begin|test|switch#} -const assert = @import("std").debug.assert; -const builtin = @import("builtin"); +const std = @import("std"); +const assert = std.debug.assert; test "switch simple" { const a: u64 = 10; @@ -3249,16 +3250,16 @@ test "switch simple" { } // Switch expressions can be used outside a function: -const os_msg = switch (builtin.os) { - builtin.Os.linux => "we found a linux user", +const os_msg = switch (std.Target.current.os.tag) { + .linux => "we found a linux user", else => "not a linux user", }; // Inside a function, switch statements implicitly are compile-time // evaluated if the target expression is compile-time known. test "switch inside function" { - switch (builtin.os) { - builtin.Os.fuchsia => { + switch (std.Target.current.os.tag) { + .fuchsia => { // On an OS other than fuchsia, block is not even analyzed, // so this compile error is not triggered. // On fuchsia this compile error would be triggered. @@ -7364,8 +7365,6 @@ test "main" { the {#syntax#}export{#endsyntax#} keyword used on a function:

{#code_begin|obj#} -const builtin = @import("builtin"); - comptime { @export(internalName, .{ .name = "foo", .linkage = .Strong }); } @@ -9397,7 +9396,7 @@ const separator = if (builtin.os == builtin.Os.windows) '\\' else '/';

{#code_begin|test|detect_test#} const std = @import("std"); -const builtin = @import("builtin"); +const builtin = std.builtin; const assert = std.debug.assert; test "builtin.is_test" { @@ -9715,7 +9714,8 @@ WebAssembly.instantiate(typedArray, {
$ node test.js
 The result is 3
{#header_open|WASI#} -

Zig's support for WebAssembly System Interface (WASI) is under active development. Example of using the standard library and reading command line arguments:

+

Zig's support for WebAssembly System Interface (WASI) is under active development. + Example of using the standard library and reading command line arguments:

{#code_begin|exe|wasi#} {#target_wasi#} const std = @import("std"); From 60f2f3457dd73772e7cd60bf5d90358079361d11 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 16:38:10 -0500 Subject: [PATCH 58/99] getStandardDynamicLinkerPath renamed and no allocator * `std.Target.getStandardDynamicLinkerPath` => `std.Target.standardDynamicLinkerPath` * it now takes a pointer to fixed size array rather than an allocator * `std.zig.system.NativeTargetInfo.detect` now supports reading PT_INTERP from /usr/bin/env --- lib/std/target.zig | 142 +++++++++++++++++++------------------ lib/std/zig/system.zig | 115 +++++++++++++++++++++--------- src-self-hosted/stage2.zig | 16 ++--- 3 files changed, 159 insertions(+), 114 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index 9cd65e7661..33019b6bbe 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1084,65 +1084,59 @@ pub const Target = struct { } } - /// Caller owns returned memory. - pub fn getStandardDynamicLinkerPath( - self: Target, - allocator: *mem.Allocator, - ) error{ - OutOfMemory, - UnknownDynamicLinkerPath, - TargetHasNoDynamicLinker, - }![:0]u8 { - const a = allocator; + /// The result will be a slice of `buffer`, pointing at position 0. + /// A return value of `null` means the concept of a dynamic linker is not meaningful for that target. + pub fn standardDynamicLinkerPath(self: Target, buffer: *[255]u8) ?[]u8 { + const S = struct { + fn print(b: *[255]u8, comptime fmt: []const u8, args: var) []u8 { + return std.fmt.bufPrint(b, fmt, args) catch unreachable; + } + fn copy(b: *[255]u8, s: []const u8) []u8 { + mem.copy(u8, b, s); + return b[0..s.len]; + } + }; + const print = S.print; + const copy = S.copy; + if (self.isAndroid()) { - return mem.dupeZ(a, u8, if (self.cpu.arch.ptrBitWidth() == 64) - "/system/bin/linker64" - else - "/system/bin/linker"); + const suffix = if (self.cpu.arch.ptrBitWidth() == 64) "64" else ""; + return print(buffer, "/system/bin/linker{}", .{suffix}); } if (self.isMusl()) { - var result = try std.Buffer.init(allocator, "/lib/ld-musl-"); - defer result.deinit(); - - var is_arm = false; - switch (self.cpu.arch) { - .arm, .thumb => { - try result.append("arm"); - is_arm = true; - }, - .armeb, .thumbeb => { - try result.append("armeb"); - is_arm = true; - }, - else => |arch| try result.append(@tagName(arch)), - } - if (is_arm and self.getFloatAbi() == .hard) { - try result.append("hf"); - } - try result.append(".so.1"); - return result.toOwnedSlice(); + const is_arm = switch (self.cpu.arch) { + .arm, .armeb, .thumb, .thumbeb => true, + else => false, + }; + const arch_part = switch (self.cpu.arch) { + .arm, .thumb => "arm", + .armeb, .thumbeb => "armeb", + else => |arch| @tagName(arch), + }; + const arch_suffix = if (is_arm and self.getFloatAbi() == .hard) "hf" else ""; + return print(buffer, "/lib/ld-musl-{}{}.so.1", .{ arch_part, arch_suffix }); } switch (self.os.tag) { - .freebsd => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.1"), - .netbsd => return mem.dupeZ(a, u8, "/libexec/ld.elf_so"), - .dragonfly => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.2"), + .freebsd => return copy(buffer, "/libexec/ld-elf.so.1"), + .netbsd => return copy(buffer, "/libexec/ld.elf_so"), + .dragonfly => return copy(buffer, "/libexec/ld-elf.so.2"), .linux => switch (self.cpu.arch) { .i386, .sparc, .sparcel, - => return mem.dupeZ(a, u8, "/lib/ld-linux.so.2"), + => return copy(buffer, "/lib/ld-linux.so.2"), - .aarch64 => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64.so.1"), - .aarch64_be => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64_be.so.1"), - .aarch64_32 => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64_32.so.1"), + .aarch64 => return copy(buffer, "/lib/ld-linux-aarch64.so.1"), + .aarch64_be => return copy(buffer, "/lib/ld-linux-aarch64_be.so.1"), + .aarch64_32 => return copy(buffer, "/lib/ld-linux-aarch64_32.so.1"), .arm, .armeb, .thumb, .thumbeb, - => return mem.dupeZ(a, u8, switch (self.getFloatAbi()) { + => return copy(buffer, switch (self.getFloatAbi()) { .hard => "/lib/ld-linux-armhf.so.3", else => "/lib/ld-linux.so.3", }), @@ -1159,29 +1153,35 @@ pub const Target = struct { }; const is_nan_2008 = mips.featureSetHas(self.cpu.features, .nan2008); const loader = if (is_nan_2008) "ld-linux-mipsn8.so.1" else "ld.so.1"; - return std.fmt.allocPrint0(a, "/lib{}/{}", .{ lib_suffix, loader }); + return print(buffer, "/lib{}/{}", .{ lib_suffix, loader }); }, - .powerpc => return mem.dupeZ(a, u8, "/lib/ld.so.1"), - .powerpc64, .powerpc64le => return mem.dupeZ(a, u8, "/lib64/ld64.so.2"), - .s390x => return mem.dupeZ(a, u8, "/lib64/ld64.so.1"), - .sparcv9 => return mem.dupeZ(a, u8, "/lib64/ld-linux.so.2"), - .x86_64 => return mem.dupeZ(a, u8, switch (self.abi) { + .powerpc => return copy(buffer, "/lib/ld.so.1"), + .powerpc64, .powerpc64le => return copy(buffer, "/lib64/ld64.so.2"), + .s390x => return copy(buffer, "/lib64/ld64.so.1"), + .sparcv9 => return copy(buffer, "/lib64/ld-linux.so.2"), + .x86_64 => return copy(buffer, switch (self.abi) { .gnux32 => "/libx32/ld-linux-x32.so.2", else => "/lib64/ld-linux-x86-64.so.2", }), - .riscv32 => return mem.dupeZ(a, u8, "/lib/ld-linux-riscv32-ilp32.so.1"), - .riscv64 => return mem.dupeZ(a, u8, "/lib/ld-linux-riscv64-lp64.so.1"), + .riscv32 => return copy(buffer, "/lib/ld-linux-riscv32-ilp32.so.1"), + .riscv64 => return copy(buffer, "/lib/ld-linux-riscv64-lp64.so.1"), + // Architectures in this list have been verified as not having a standard + // dynamic linker path. .wasm32, .wasm64, - => return error.TargetHasNoDynamicLinker, - - .arc, - .avr, .bpfel, .bpfeb, + .nvptx, + .nvptx64, + => return null, + + // TODO go over each item in this list and either move it to the above list, or + // implement the standard dynamic linker path code for it. + .arc, + .avr, .hexagon, .msp430, .r600, @@ -1189,8 +1189,6 @@ pub const Target = struct { .tce, .tcele, .xcore, - .nvptx, - .nvptx64, .le32, .le64, .amdil, @@ -1204,9 +1202,25 @@ pub const Target = struct { .lanai, .renderscript32, .renderscript64, - => return error.UnknownDynamicLinkerPath, + => return null, }, + // Operating systems in this list have been verified as not having a standard + // dynamic linker path. + .freestanding, + .ios, + .tvos, + .watchos, + .macosx, + .uefi, + .windows, + .emscripten, + .other, + .wasi, + => return null, + + // TODO go over each item in this list and either move it to the above list, or + // implement the standard dynamic linker path code for it. .ananas, .cloudabi, .fuchsia, @@ -1230,19 +1244,7 @@ pub const Target = struct { .amdpal, .hermit, .hurd, - => return error.UnknownDynamicLinkerPath, - - .freestanding, - .ios, - .tvos, - .watchos, - .macosx, - .uefi, - .windows, - .emscripten, - .other, - .wasi, - => return error.TargetHasNoDynamicLinker, + => return null, } } }; diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 5c9b71001b..d2828a88a1 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -167,7 +167,15 @@ pub const NativePaths = struct { pub const NativeTargetInfo = struct { target: Target, - dynamic_linker: ?[:0]u8, + + /// Contains the memory used to store the dynamic linker path. This field should + /// not be used directly. See `dynamicLinker` and `setDynamicLinker`. This field + /// exists so that this API requires no allocator. + dynamic_linker_buffer: [255]u8 = undefined, + + /// Used to construct the dynamic linker path. This field should not be used + /// directly. See `dynamicLinker` and `setDynamicLinker`. + dynamic_linker_max: ?u8 = null, pub const DetectError = error{ OutOfMemory, @@ -181,6 +189,7 @@ pub const NativeTargetInfo = struct { /// Detects the native CPU model & features, operating system & version, and C ABI & dynamic linker. /// On Linux, this is additionally responsible for detecting the native glibc version when applicable. + /// TODO Remove the allocator requirement from this. pub fn detect(allocator: *Allocator) DetectError!NativeTargetInfo { const arch = Target.current.cpu.arch; const os_tag = Target.current.os.tag; @@ -188,8 +197,7 @@ pub const NativeTargetInfo = struct { // TODO Detect native CPU model & features. Until that is implemented we hard code baseline. const cpu = Target.Cpu.baseline(arch); - // TODO Detect native operating system version. Until that is implemented we use the minimum version - // of the default range. + // TODO Detect native operating system version. Until that is implemented we use the default range. const os = Target.Os.defaultVersionRange(os_tag); return detectAbiAndDynamicLinker(allocator, cpu, os); @@ -201,6 +209,21 @@ pub const NativeTargetInfo = struct { self.* = undefined; } + /// The returned memory has the same lifetime as the `NativeTargetInfo`. + pub fn dynamicLinker(self: *const NativeTargetInfo) ?[]const u8 { + const m = self.dynamic_linker_max orelse return null; + return self.dynamic_linker_buffer[0 .. m + 1]; + } + + pub fn setDynamicLinker(self: *NativeTargetInfo, dl_or_null: ?[]const u8) void { + if (dl_or_null) |dl| { + mem.copy(u8, &self.dynamic_linker_buffer, dl); + self.dynamic_linker_max = @intCast(u8, dl.len - 1); + } else { + self.dynamic_linker_max = null; + } + } + /// First we attempt to use the executable's own binary. If it is dynamically /// linked, then it should answer both the C ABI question and the dynamic linker question. /// If it is statically linked, then we try /usr/bin/env. If that does not provide the answer, then @@ -245,10 +268,11 @@ pub const NativeTargetInfo = struct { .os = os, .abi = abi, }; - const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue, - }; + var buf: [255]u8 = undefined; + const standard_ld_path = if (target.standardDynamicLinkerPath(&buf)) |s| + try mem.dupe(allocator, u8, s) + else + continue; errdefer allocator.free(standard_ld_path); try ld_info_list.append(.{ .ld_path = standard_ld_path, @@ -294,28 +318,29 @@ pub const NativeTargetInfo = struct { } } - return NativeTargetInfo{ + var result: NativeTargetInfo = .{ .target = .{ .cpu = cpu, .os = os_adjusted, .abi = found_ld_info.abi, }, - .dynamic_linker = try mem.dupeZ(allocator, u8, found_ld_path), }; + result.setDynamicLinker(found_ld_path); + return result; } // If Zig is statically linked, such as via distributed binary static builds, the above // trick won't work. The next thing we fall back to is the same thing, but for /usr/bin/env. // Since that path is hard-coded into the shebang line of many portable scripts, it's a // reasonably reliable path to check for. - return abiAndDynamicLinkerFromUsrBinEnv(allocator, cpu, os) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.FileSystem => return error.FileSystem, - error.SystemResources => return error.SystemResources, - error.SymLinkLoop => return error.SymLinkLoop, - error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded, - error.SystemFdQuotaExceeded => return error.SystemFdQuotaExceeded, - error.DeviceBusy => return error.DeviceBusy, + return abiAndDynamicLinkerFromUsrBinEnv(cpu, os) catch |err| switch (err) { + error.FileSystem, + error.SystemResources, + error.SymLinkLoop, + error.ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded, + error.DeviceBusy, + => |e| return e, error.UnableToReadElfFile, error.ElfNotADynamicExecutable, @@ -327,6 +352,8 @@ pub const NativeTargetInfo = struct { error.InvalidElfMagic, error.UsrBinEnvNotAvailable, error.Unexpected, + error.UnexpectedEndOfFile, + error.NameTooLong, // Finally, we fall back on the standard path. => defaultAbiAndDynamicLinker(allocator, cpu, os), }; @@ -362,11 +389,7 @@ pub const NativeTargetInfo = struct { }; } - fn abiAndDynamicLinkerFromUsrBinEnv( - allocator: *Allocator, - cpu: Target.Cpu, - os: Target.Os, - ) !NativeTargetInfo { + fn abiAndDynamicLinkerFromUsrBinEnv(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { const env_file = std.fs.openFileAbsoluteC("/usr/bin/env", .{}) catch |err| switch (err) { error.NoSpaceLeft => unreachable, error.NameTooLong => unreachable, @@ -409,6 +432,14 @@ pub const NativeTargetInfo = struct { const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum); const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); + var result: NativeTargetInfo = .{ + .target = .{ + .cpu = cpu, + .os = os, + .abi = Target.Abi.default(cpu.arch, os), + }, + }; + const ph_total_size = std.math.mul(u32, phentsize, phnum) catch |err| switch (err) { error.Overflow => return error.InvalidElfProgramHeaders, }; @@ -430,7 +461,22 @@ pub const NativeTargetInfo = struct { const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type); switch (p_type) { elf.PT_INTERP => { - std.debug.warn("found PT_INTERP\n", .{}); + const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); + const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); + var interp_buf: [255]u8 = undefined; + if (p_filesz > interp_buf.len) return error.NameTooLong; + var read_offset: usize = 0; + while (true) { + const len = try wrapRead(env_file.pread( + interp_buf[read_offset .. p_filesz - read_offset], + p_offset + read_offset, + )); + if (len == 0) return error.UnexpectedEndOfFile; + read_offset += len; + if (read_offset == p_filesz) break; + } + // PT_INTERP includes a null byte in p_filesz. + result.setDynamicLinker(interp_buf[0 .. p_filesz - 1]); }, elf.PT_DYNAMIC => { std.debug.warn("found PT_DYNAMIC\n", .{}); @@ -440,7 +486,7 @@ pub const NativeTargetInfo = struct { } } - return error.OutOfMemory; // TODO + return result; } fn wrapRead(res: std.os.ReadError!usize) !usize { @@ -457,18 +503,17 @@ pub const NativeTargetInfo = struct { } fn defaultAbiAndDynamicLinker(allocator: *Allocator, cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { - const target: Target = .{ - .cpu = cpu, - .os = os, - .abi = Target.Abi.default(cpu.arch, os), - }; - return @as(NativeTargetInfo, .{ - .target = target, - .dynamic_linker = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => null, + var result: NativeTargetInfo = .{ + .target = .{ + .cpu = cpu, + .os = os, + .abi = Target.Abi.default(cpu.arch, os), }, - }); + }; + if (result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer)) |s| { + result.dynamic_linker_max = @intCast(u8, s.len - 1); + } + return result; } }; diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 14673b0e98..5358c4fedc 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1157,9 +1157,9 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) if (cross_target.os_tag == null) { adjusted_target.os = detected_info.target.os; - if (detected_info.dynamic_linker) |dl| { + if (detected_info.dynamicLinker()) |dl| { have_native_dl = true; - dynamic_linker_ptr.* = dl.ptr; + dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl); } if (cross_target.abi == null) { adjusted_target.abi = detected_info.target.abi; @@ -1169,13 +1169,11 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) } } if (!have_native_dl) { - dynamic_linker_ptr.* = adjusted_target.getStandardDynamicLinkerPath( - std.heap.c_allocator, - ) catch |err| switch (err) { - error.TargetHasNoDynamicLinker => null, - error.UnknownDynamicLinkerPath => null, - else => |e| return e, - }; + var buf: [255]u8 = undefined; + dynamic_linker_ptr.* = if (adjusted_target.standardDynamicLinkerPath(&buf)) |s| + try mem.dupeZ(std.heap.c_allocator, u8, s) + else + null; } return adjusted_target; } From fd006c1c74a43827a6f1c32e289ba57cafa874be Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 17:20:40 -0500 Subject: [PATCH 59/99] std.zig.system.NativeTargetInfo.detect: almost no Allocator --- lib/std/process.zig | 4 +++ lib/std/target.zig | 12 ++++---- lib/std/zig/system.zig | 62 ++++++++++++++++++-------------------- src-self-hosted/stage2.zig | 4 +-- 4 files changed, 42 insertions(+), 40 deletions(-) diff --git a/lib/std/process.zig b/lib/std/process.zig index 118b5d9ab4..01b9947518 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -609,6 +609,10 @@ pub fn getBaseAddress() usize { } /// Caller owns the result value and each inner slice. +/// TODO Remove the `Allocator` requirement from this API, which will remove the `Allocator` +/// requirement from `std.zig.system.NativeTargetInfo.detect`. Most likely this will require +/// introducing a new, lower-level function which takes a callback function, and then this +/// function which takes an allocator can exist on top of it. pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]u8 { switch (builtin.link_mode) { .Static => return &[_][:0]u8{}, diff --git a/lib/std/target.zig b/lib/std/target.zig index 33019b6bbe..379ae72afe 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1084,16 +1084,16 @@ pub const Target = struct { } } - /// The result will be a slice of `buffer`, pointing at position 0. + /// The result will be a byte index *pointing at the final byte*. In other words, length minus one. /// A return value of `null` means the concept of a dynamic linker is not meaningful for that target. - pub fn standardDynamicLinkerPath(self: Target, buffer: *[255]u8) ?[]u8 { + pub fn standardDynamicLinkerPath(self: Target, buffer: *[255]u8) ?u8 { const S = struct { - fn print(b: *[255]u8, comptime fmt: []const u8, args: var) []u8 { - return std.fmt.bufPrint(b, fmt, args) catch unreachable; + fn print(b: *[255]u8, comptime fmt: []const u8, args: var) u8 { + return @intCast(u8, (std.fmt.bufPrint(b, fmt, args) catch unreachable).len - 1); } - fn copy(b: *[255]u8, s: []const u8) []u8 { + fn copy(b: *[255]u8, s: []const u8) u8 { mem.copy(u8, b, s); - return b[0..s.len]; + return @intCast(u8, s.len - 1); } }; const print = S.print; diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index d2828a88a1..e8c95f5125 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -189,7 +189,9 @@ pub const NativeTargetInfo = struct { /// Detects the native CPU model & features, operating system & version, and C ABI & dynamic linker. /// On Linux, this is additionally responsible for detecting the native glibc version when applicable. - /// TODO Remove the allocator requirement from this. + /// Any resources this function allocates are released before returning, and so there is no + /// deinitialization method. + /// TODO Remove the Allocator requirement from this function. pub fn detect(allocator: *Allocator) DetectError!NativeTargetInfo { const arch = Target.current.cpu.arch; const os_tag = Target.current.os.tag; @@ -203,15 +205,9 @@ pub const NativeTargetInfo = struct { return detectAbiAndDynamicLinker(allocator, cpu, os); } - /// Must be the same `Allocator` passed to `detect`. - pub fn deinit(self: *NativeTargetInfo, allocator: *Allocator) void { - if (self.dynamic_linker) |dl| allocator.free(dl); - self.* = undefined; - } - /// The returned memory has the same lifetime as the `NativeTargetInfo`. pub fn dynamicLinker(self: *const NativeTargetInfo) ?[]const u8 { - const m = self.dynamic_linker_max orelse return null; + const m: usize = self.dynamic_linker_max orelse return null; return self.dynamic_linker_buffer[0 .. m + 1]; } @@ -228,13 +224,14 @@ pub const NativeTargetInfo = struct { /// linked, then it should answer both the C ABI question and the dynamic linker question. /// If it is statically linked, then we try /usr/bin/env. If that does not provide the answer, then /// we fall back to the defaults. + /// TODO Remove the Allocator requirement from this function. fn detectAbiAndDynamicLinker( allocator: *Allocator, cpu: Target.Cpu, os: Target.Os, ) DetectError!NativeTargetInfo { if (!comptime Target.current.hasDynamicLinker()) { - return defaultAbiAndDynamicLinker(allocator, cpu, os); + return defaultAbiAndDynamicLinker(cpu, os); } // The current target's ABI cannot be relied on for this. For example, we may build the zig // compiler for target riscv64-linux-musl and provide a tarball for users to download. @@ -242,15 +239,15 @@ pub const NativeTargetInfo = struct { // and supported by Zig. But that means that we must detect the system ABI here rather than // relying on `Target.current`. const LdInfo = struct { - ld_path: []u8, + ld_path_buffer: [255]u8, + ld_path_max: u8, abi: Target.Abi, - }; - var ld_info_list = std.ArrayList(LdInfo).init(allocator); - defer { - for (ld_info_list.toSlice()) |ld_info| allocator.free(ld_info.ld_path); - ld_info_list.deinit(); - } + pub fn ldPath(self: *const @This()) []const u8 { + const m: usize = self.ld_path_max; + return self.ld_path_buffer[0 .. m + 1]; + } + }; const all_abis = comptime blk: { assert(@enumToInt(Target.Abi.none) == 0); const fields = std.meta.fields(Target.Abi)[1..]; @@ -260,6 +257,9 @@ pub const NativeTargetInfo = struct { } break :blk array; }; + var ld_info_list_buffer: [all_abis.len]LdInfo = undefined; + var ld_info_list_len: usize = 0; + for (all_abis) |abi| { // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and // skip adding it to `ld_info_list`. @@ -268,17 +268,17 @@ pub const NativeTargetInfo = struct { .os = os, .abi = abi, }; - var buf: [255]u8 = undefined; - const standard_ld_path = if (target.standardDynamicLinkerPath(&buf)) |s| - try mem.dupe(allocator, u8, s) - else - continue; - errdefer allocator.free(standard_ld_path); - try ld_info_list.append(.{ - .ld_path = standard_ld_path, + const ld_info = &ld_info_list_buffer[ld_info_list_len]; + ld_info_list_len += 1; + + ld_info.* = .{ + .ld_path_buffer = undefined, + .ld_path_max = undefined, .abi = abi, - }); + }; + ld_info.ld_path_max = target.standardDynamicLinkerPath(&ld_info.ld_path_buffer) orelse continue; } + const ld_info_list = ld_info_list_buffer[0..ld_info_list_len]; // Best case scenario: the executable is dynamically linked, and we can iterate // over our own shared objects and find a dynamic linker. @@ -292,8 +292,8 @@ pub const NativeTargetInfo = struct { // Look for dynamic linker. // This is O(N^M) but typical case here is N=2 and M=10. find_ld: for (lib_paths) |lib_path| { - for (ld_info_list.toSlice()) |ld_info| { - const standard_ld_basename = fs.path.basename(ld_info.ld_path); + for (ld_info_list) |ld_info| { + const standard_ld_basename = fs.path.basename(ld_info.ldPath()); if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) { found_ld_info = ld_info; found_ld_path = lib_path; @@ -355,7 +355,7 @@ pub const NativeTargetInfo = struct { error.UnexpectedEndOfFile, error.NameTooLong, // Finally, we fall back on the standard path. - => defaultAbiAndDynamicLinker(allocator, cpu, os), + => defaultAbiAndDynamicLinker(cpu, os), }; } @@ -502,7 +502,7 @@ pub const NativeTargetInfo = struct { }; } - fn defaultAbiAndDynamicLinker(allocator: *Allocator, cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { + fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { var result: NativeTargetInfo = .{ .target = .{ .cpu = cpu, @@ -510,9 +510,7 @@ pub const NativeTargetInfo = struct { .abi = Target.Abi.default(cpu.arch, os), }, }; - if (result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer)) |s| { - result.dynamic_linker_max = @intCast(u8, s.len - 1); - } + result.dynamic_linker_max = result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer); return result; } }; diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 5358c4fedc..cd3f49edfa 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1170,8 +1170,8 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) } if (!have_native_dl) { var buf: [255]u8 = undefined; - dynamic_linker_ptr.* = if (adjusted_target.standardDynamicLinkerPath(&buf)) |s| - try mem.dupeZ(std.heap.c_allocator, u8, s) + dynamic_linker_ptr.* = if (adjusted_target.standardDynamicLinkerPath(&buf)) |m| + try mem.dupeZ(std.heap.c_allocator, u8, buf[0 .. @as(usize, m) + 1]) else null; } From 3683ba87ac5e5dfb6ea65c21c9fb714ed0582124 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Feb 2020 19:49:00 -0500 Subject: [PATCH 60/99] complete the native target detection based on /usr/bin/env --- lib/std/c.zig | 1 + lib/std/dynamic_library.zig | 4 +- lib/std/elf.zig | 10 +- lib/std/zig/system.zig | 306 ++++++++++++++++++++++++++++-------- 4 files changed, 244 insertions(+), 77 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 9072d2c7cd..9d7d9524d6 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -108,6 +108,7 @@ pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8 pub extern "c" fn dup(fd: fd_t) c_int; pub extern "c" fn dup2(old_fd: fd_t, new_fd: fd_t) c_int; pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize; +pub extern "c" fn readlinkat(dirfd: fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize; pub extern "c" fn realpath(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8; pub extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int; pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index 34f45894fb..0d14f8d032 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -82,12 +82,12 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator { for (dyn_table) |*dyn| { switch (dyn.d_tag) { elf.DT_DEBUG => { - const r_debug = @intToPtr(*RDebug, dyn.d_un.d_ptr); + const r_debug = @intToPtr(*RDebug, dyn.d_val); if (r_debug.r_version != 1) return error.InvalidExe; break :init r_debug.r_map; }, elf.DT_PLTGOT => { - const got_table = @intToPtr([*]usize, dyn.d_un.d_ptr); + const got_table = @intToPtr([*]usize, dyn.d_val); // The address to the link_map structure is stored in the // second slot break :init @intToPtr(?*LinkMap, got_table[1]); diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 007a01bb90..99084f1897 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -708,17 +708,11 @@ pub const Elf64_Rela = extern struct { }; pub const Elf32_Dyn = extern struct { d_tag: Elf32_Sword, - d_un: extern union { - d_val: Elf32_Word, - d_ptr: Elf32_Addr, - }, + d_val: Elf32_Addr, }; pub const Elf64_Dyn = extern struct { d_tag: Elf64_Sxword, - d_un: extern union { - d_val: Elf64_Xword, - d_ptr: Elf64_Addr, - }, + d_val: Elf64_Addr, }; pub const Elf32_Verdef = extern struct { vd_version: Elf32_Half, diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index e8c95f5125..fe2f4c7e04 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -238,16 +238,6 @@ pub const NativeTargetInfo = struct { // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined // and supported by Zig. But that means that we must detect the system ABI here rather than // relying on `Target.current`. - const LdInfo = struct { - ld_path_buffer: [255]u8, - ld_path_max: u8, - abi: Target.Abi, - - pub fn ldPath(self: *const @This()) []const u8 { - const m: usize = self.ld_path_max; - return self.ld_path_buffer[0 .. m + 1]; - } - }; const all_abis = comptime blk: { assert(@enumToInt(Target.Abi.none) == 0); const fields = std.meta.fields(Target.Abi)[1..]; @@ -333,7 +323,7 @@ pub const NativeTargetInfo = struct { // trick won't work. The next thing we fall back to is the same thing, but for /usr/bin/env. // Since that path is hard-coded into the shebang line of many portable scripts, it's a // reasonably reliable path to check for. - return abiAndDynamicLinkerFromUsrBinEnv(cpu, os) catch |err| switch (err) { + return abiAndDynamicLinkerFromUsrBinEnv(cpu, os, ld_info_list) catch |err| switch (err) { error.FileSystem, error.SystemResources, error.SymLinkLoop, @@ -343,8 +333,6 @@ pub const NativeTargetInfo = struct { => |e| return e, error.UnableToReadElfFile, - error.ElfNotADynamicExecutable, - error.InvalidElfProgramHeaders, error.InvalidElfClass, error.InvalidElfVersion, error.InvalidElfEndian, @@ -373,6 +361,10 @@ pub const NativeTargetInfo = struct { error.NotDir => return error.GnuLibCVersionUnavailable, error.Unexpected => return error.GnuLibCVersionUnavailable, }; + return glibcVerFromLinkName(link_name); + } + + fn glibcVerFromLinkName(link_name: []const u8) !std.builtin.Version { // example: "libc-2.3.4.so" // example: "libc-2.27.so" const prefix = "libc-"; @@ -389,7 +381,11 @@ pub const NativeTargetInfo = struct { }; } - fn abiAndDynamicLinkerFromUsrBinEnv(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { + fn abiAndDynamicLinkerFromUsrBinEnv( + cpu: Target.Cpu, + os: Target.Os, + ld_info_list: []const LdInfo, + ) !NativeTargetInfo { const env_file = std.fs.openFileAbsoluteC("/usr/bin/env", .{}) catch |err| switch (err) { error.NoSpaceLeft => unreachable, error.NameTooLong => unreachable, @@ -409,8 +405,7 @@ pub const NativeTargetInfo = struct { else => |e| return e, }; var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined; - const hdr_bytes_len = try wrapRead(env_file.pread(&hdr_buf, 0)); - if (hdr_bytes_len < @sizeOf(elf.Elf32_Ehdr)) return error.InvalidElfFile; + _ = try preadFull(env_file, &hdr_buf, 0, hdr_buf.len); const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf); const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf); if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; @@ -430,7 +425,6 @@ pub const NativeTargetInfo = struct { var phoff = elfInt(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff); const phentsize = elfInt(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize); const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum); - const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); var result: NativeTargetInfo = .{ .target = .{ @@ -439,67 +433,234 @@ pub const NativeTargetInfo = struct { .abi = Target.Abi.default(cpu.arch, os), }, }; + var rpath_offset: ?u64 = null; // Found inside PT_DYNAMIC - const ph_total_size = std.math.mul(u32, phentsize, phnum) catch |err| switch (err) { - error.Overflow => return error.InvalidElfProgramHeaders, - }; var ph_buf: [16 * @sizeOf(elf.Elf64_Phdr)]u8 align(@alignOf(elf.Elf64_Phdr)) = undefined; + if (phentsize > @sizeOf(elf.Elf64_Phdr)) return error.InvalidElfFile; + var ph_i: u16 = 0; while (ph_i < phnum) { - // Reserve some bytes so that we can deref the 64-bit struct fields even when the ELF file is 32-bits. - const reserve = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr); - const read_byte_len = try wrapRead(env_file.pread(ph_buf[0 .. ph_buf.len - reserve], phoff)); - if (read_byte_len < phentsize) return error.ElfNotADynamicExecutable; - var buf_i: usize = 0; - while (buf_i < read_byte_len and ph_i < phnum) : ({ + // Reserve some bytes so that we can deref the 64-bit struct fields + // even when the ELF file is 32-bits. + const ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr); + const ph_read_byte_len = try preadFull(env_file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize); + var ph_buf_i: usize = 0; + while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({ ph_i += 1; phoff += phentsize; - buf_i += phentsize; + ph_buf_i += phentsize; }) { - const ph32 = @ptrCast(*elf.Elf32_Phdr, @alignCast(@alignOf(elf.Elf32_Phdr), &ph_buf[buf_i])); - const ph64 = @ptrCast(*elf.Elf64_Phdr, @alignCast(@alignOf(elf.Elf64_Phdr), &ph_buf[buf_i])); + const ph32 = @ptrCast(*elf.Elf32_Phdr, @alignCast(@alignOf(elf.Elf32_Phdr), &ph_buf[ph_buf_i])); + const ph64 = @ptrCast(*elf.Elf64_Phdr, @alignCast(@alignOf(elf.Elf64_Phdr), &ph_buf[ph_buf_i])); const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type); switch (p_type) { elf.PT_INTERP => { const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); - var interp_buf: [255]u8 = undefined; - if (p_filesz > interp_buf.len) return error.NameTooLong; - var read_offset: usize = 0; - while (true) { - const len = try wrapRead(env_file.pread( - interp_buf[read_offset .. p_filesz - read_offset], - p_offset + read_offset, - )); - if (len == 0) return error.UnexpectedEndOfFile; - read_offset += len; - if (read_offset == p_filesz) break; - } + if (p_filesz > result.dynamic_linker_buffer.len) return error.NameTooLong; + _ = try preadFull(env_file, result.dynamic_linker_buffer[0..p_filesz], p_offset, p_filesz); // PT_INTERP includes a null byte in p_filesz. - result.setDynamicLinker(interp_buf[0 .. p_filesz - 1]); + const len = p_filesz - 1; + // dynamic_linker_max is "max", not "len". + // We know it will fit in u8 because we check against dynamic_linker_buffer.len above. + result.dynamic_linker_max = @intCast(u8, len - 1); + + // Use it to determine ABI. + const full_ld_path = result.dynamic_linker_buffer[0..len]; + for (ld_info_list) |ld_info| { + const standard_ld_basename = fs.path.basename(ld_info.ldPath()); + if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) { + result.target.abi = ld_info.abi; + break; + } + } }, - elf.PT_DYNAMIC => { - std.debug.warn("found PT_DYNAMIC\n", .{}); + // We only need this for detecting glibc version. + elf.PT_DYNAMIC => if (Target.current.os.tag == .linux and result.target.isGnuLibC()) { + var dyn_off = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); + const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); + const dyn_size: u64 = if (is_64) @sizeOf(elf.Elf64_Dyn) else @sizeOf(elf.Elf32_Dyn); + const dyn_num = p_filesz / dyn_size; + var dyn_buf: [16 * @sizeOf(elf.Elf64_Dyn)]u8 align(@alignOf(elf.Elf64_Dyn)) = undefined; + var dyn_i: usize = 0; + dyn: while (dyn_i < dyn_num) { + // Reserve some bytes so that we can deref the 64-bit struct fields + // even when the ELF file is 32-bits. + const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn); + const dyn_read_byte_len = try preadFull( + env_file, + dyn_buf[0 .. dyn_buf.len - dyn_reserve], + dyn_off, + dyn_size, + ); + var dyn_buf_i: usize = 0; + while (dyn_buf_i < dyn_read_byte_len and dyn_i < dyn_num) : ({ + dyn_i += 1; + dyn_off += dyn_size; + dyn_buf_i += dyn_size; + }) { + const dyn32 = @ptrCast( + *elf.Elf32_Dyn, + @alignCast(@alignOf(elf.Elf32_Dyn), &dyn_buf[dyn_buf_i]), + ); + const dyn64 = @ptrCast( + *elf.Elf64_Dyn, + @alignCast(@alignOf(elf.Elf64_Dyn), &dyn_buf[dyn_buf_i]), + ); + const tag = elfInt(is_64, need_bswap, dyn32.d_tag, dyn64.d_tag); + const val = elfInt(is_64, need_bswap, dyn32.d_val, dyn64.d_val); + if (tag == elf.DT_RUNPATH) { + rpath_offset = val; + break :dyn; + } + } + } }, else => continue, } } } + if (Target.current.os.tag == .linux and result.target.isGnuLibC()) { + if (rpath_offset) |rpoff| { + const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); + + var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff); + const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize); + const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx); + + var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined; + if (sh_buf.len < shentsize) return error.InvalidElfFile; + + _ = try preadFull(env_file, &sh_buf, str_section_off, shentsize); + const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf)); + const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf)); + const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset); + const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size); + var strtab_buf: [4096:0]u8 = undefined; + const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len); + const shstrtab_read_len = try preadFull(env_file, &strtab_buf, shstrtab_off, shstrtab_len); + const shstrtab = strtab_buf[0..shstrtab_read_len]; + + const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum); + var sh_i: u16 = 0; + const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) { + // Reserve some bytes so that we can deref the 64-bit struct fields + // even when the ELF file is 32-bits. + const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr); + const sh_read_byte_len = try preadFull( + env_file, + sh_buf[0 .. sh_buf.len - sh_reserve], + shoff, + shentsize, + ); + var sh_buf_i: usize = 0; + while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({ + sh_i += 1; + shoff += shentsize; + sh_buf_i += shentsize; + }) { + const sh32 = @ptrCast( + *elf.Elf32_Shdr, + @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf[sh_buf_i]), + ); + const sh64 = @ptrCast( + *elf.Elf64_Shdr, + @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf[sh_buf_i]), + ); + const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name); + // TODO this pointer cast should not be necessary + const sh_name = mem.toSliceConst(u8, @ptrCast([*:0]u8, shstrtab[sh_name_off..].ptr)); + if (mem.eql(u8, sh_name, ".dynstr")) { + break :find_dyn_str .{ + .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset), + .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size), + }; + } + } + } else null; + + if (dynstr) |ds| { + const strtab_len = std.math.min(ds.size, strtab_buf.len); + const strtab_read_len = try preadFull(env_file, &strtab_buf, ds.offset, shstrtab_len); + const strtab = strtab_buf[0..strtab_read_len]; + // TODO this pointer cast should not be necessary + const rpath_list = mem.toSliceConst(u8, @ptrCast([*:0]u8, strtab[rpoff..].ptr)); + var it = mem.tokenize(rpath_list, ":"); + while (it.next()) |rpath| { + var dir = fs.cwd().openDirList(rpath) catch |err| switch (err) { + error.NameTooLong => unreachable, + error.InvalidUtf8 => unreachable, + error.BadPathName => unreachable, + error.DeviceBusy => unreachable, + + error.FileNotFound, + error.NotDir, + error.AccessDenied, + error.NoDevice, + => continue, + + error.ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded, + error.SystemResources, + error.SymLinkLoop, + error.Unexpected, + => |e| return e, + }; + defer dir.close(); + + var link_buf: [std.os.PATH_MAX]u8 = undefined; + const link_name = std.os.readlinkatC( + dir.fd, + glibc_so_basename, + &link_buf, + ) catch |err| switch (err) { + error.NameTooLong => unreachable, + + error.AccessDenied, + error.FileNotFound, + error.NotDir, + => continue, + + error.SystemResources, + error.FileSystem, + error.SymLinkLoop, + error.Unexpected, + => |e| return e, + }; + result.target.os.version_range.linux.glibc = glibcVerFromLinkName( + link_name, + ) catch |err| switch (err) { + error.UnrecognizedGnuLibCFileName, + error.InvalidGnuLibCVersion, + => continue, + }; + break; + } + } + } + } + return result; } - fn wrapRead(res: std.os.ReadError!usize) !usize { - return res catch |err| switch (err) { - error.OperationAborted => unreachable, // Windows-only - error.WouldBlock => unreachable, // Did not request blocking mode - error.SystemResources => return error.SystemResources, - error.IsDir => return error.UnableToReadElfFile, - error.BrokenPipe => return error.UnableToReadElfFile, - error.ConnectionResetByPeer => return error.UnableToReadElfFile, - error.Unexpected => return error.Unexpected, - error.InputOutput => return error.FileSystem, - }; + fn preadFull(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize { + var i: u64 = 0; + while (i < min_read_len) { + const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) { + error.OperationAborted => unreachable, // Windows-only + error.WouldBlock => unreachable, // Did not request blocking mode + error.SystemResources => return error.SystemResources, + error.IsDir => return error.UnableToReadElfFile, + error.BrokenPipe => return error.UnableToReadElfFile, + error.ConnectionResetByPeer => return error.UnableToReadElfFile, + error.Unexpected => return error.Unexpected, + error.InputOutput => return error.FileSystem, + }; + if (len == 0) return error.UnexpectedEndOfFile; + i += len; + } + return i; } fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { @@ -513,20 +674,31 @@ pub const NativeTargetInfo = struct { result.dynamic_linker_max = result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer); return result; } -}; -fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) { - if (is_64) { - if (need_bswap) { - return @byteSwap(@TypeOf(int_64), int_64); - } else { - return int_64; + const LdInfo = struct { + ld_path_buffer: [255]u8, + ld_path_max: u8, + abi: Target.Abi, + + pub fn ldPath(self: *const LdInfo) []const u8 { + const m: usize = self.ld_path_max; + return self.ld_path_buffer[0 .. m + 1]; } - } else { - if (need_bswap) { - return @byteSwap(@TypeOf(int_32), int_32); + }; + + fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) { + if (is_64) { + if (need_bswap) { + return @byteSwap(@TypeOf(int_64), int_64); + } else { + return int_64; + } } else { - return int_32; + if (need_bswap) { + return @byteSwap(@TypeOf(int_32), int_32); + } else { + return int_32; + } } } -} +}; From d45ea4d89d7232fc4bcf56a5e088d7d6b5004ebb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 02:00:20 -0500 Subject: [PATCH 61/99] stage1: make get_native_target go through self-hosted --- lib/std/special/init-exe/src/main.zig | 2 +- lib/std/target.zig | 2 +- src/main.cpp | 10 ++- src/stage2.cpp | 100 ++++++++++++++++++++++++++ src/target.cpp | 100 -------------------------- src/target.hpp | 1 - 6 files changed, 110 insertions(+), 105 deletions(-) diff --git a/lib/std/special/init-exe/src/main.zig b/lib/std/special/init-exe/src/main.zig index 5f35540dc0..c6a70af56d 100644 --- a/lib/std/special/init-exe/src/main.zig +++ b/lib/std/special/init-exe/src/main.zig @@ -1,5 +1,5 @@ const std = @import("std"); pub fn main() anyerror!void { - std.debug.warn("All your base are belong to us.\n", .{}); + std.debug.warn("All your codebase are belong to us.\n", .{}); } diff --git a/lib/std/target.zig b/lib/std/target.zig index 379ae72afe..a72bd921b0 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1215,8 +1215,8 @@ pub const Target = struct { .uefi, .windows, .emscripten, - .other, .wasi, + .other, => return null, // TODO go over each item in this list and either move it to the above list, or diff --git a/src/main.cpp b/src/main.cpp index 11612402ee..f741cafd40 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -500,7 +500,10 @@ static int main0(int argc, char **argv) { os_path_join(get_zig_special_dir(zig_lib_dir), buf_create_from_str("build_runner.zig"), build_runner_path); ZigTarget target; - get_native_target(&target); + if ((err = target_parse_triple(&target, "native", nullptr))) { + fprintf(stderr, "Unable to get native target: %s\n", err_str(err)); + return EXIT_FAILURE; + } Buf *build_file_buf = buf_create_from_str((build_file != nullptr) ? build_file : "build.zig"); Buf build_file_abs = os_path_resolve(&build_file_buf, 1); @@ -1337,7 +1340,10 @@ static int main0(int argc, char **argv) { return main_exit(root_progress_node, EXIT_SUCCESS); } else if (cmd == CmdTest) { ZigTarget native; - get_native_target(&native); + if ((err = target_parse_triple(&native, "native", nullptr))) { + fprintf(stderr, "Unable to get native target: %s\n", err_str(err)); + return EXIT_FAILURE; + } g->enable_cache = get_cache_opt(enable_cache, output_dir == nullptr); codegen_build_and_link(g); diff --git a/src/stage2.cpp b/src/stage2.cpp index 2023b45aaf..bc8e65fd56 100644 --- a/src/stage2.cpp +++ b/src/stage2.cpp @@ -91,6 +91,106 @@ void stage2_progress_complete_one(Stage2ProgressNode *node) {} void stage2_progress_disable_tty(Stage2Progress *progress) {} void stage2_progress_update_node(Stage2ProgressNode *node, size_t completed_count, size_t estimated_total_items){} +static Os get_zig_os_type(ZigLLVM_OSType os_type) { + switch (os_type) { + case ZigLLVM_UnknownOS: + return OsFreestanding; + case ZigLLVM_Ananas: + return OsAnanas; + case ZigLLVM_CloudABI: + return OsCloudABI; + case ZigLLVM_DragonFly: + return OsDragonFly; + case ZigLLVM_FreeBSD: + return OsFreeBSD; + case ZigLLVM_Fuchsia: + return OsFuchsia; + case ZigLLVM_IOS: + return OsIOS; + case ZigLLVM_KFreeBSD: + return OsKFreeBSD; + case ZigLLVM_Linux: + return OsLinux; + case ZigLLVM_Lv2: + return OsLv2; + case ZigLLVM_Darwin: + case ZigLLVM_MacOSX: + return OsMacOSX; + case ZigLLVM_NetBSD: + return OsNetBSD; + case ZigLLVM_OpenBSD: + return OsOpenBSD; + case ZigLLVM_Solaris: + return OsSolaris; + case ZigLLVM_Win32: + return OsWindows; + case ZigLLVM_Haiku: + return OsHaiku; + case ZigLLVM_Minix: + return OsMinix; + case ZigLLVM_RTEMS: + return OsRTEMS; + case ZigLLVM_NaCl: + return OsNaCl; + case ZigLLVM_CNK: + return OsCNK; + case ZigLLVM_AIX: + return OsAIX; + case ZigLLVM_CUDA: + return OsCUDA; + case ZigLLVM_NVCL: + return OsNVCL; + case ZigLLVM_AMDHSA: + return OsAMDHSA; + case ZigLLVM_PS4: + return OsPS4; + case ZigLLVM_ELFIAMCU: + return OsELFIAMCU; + case ZigLLVM_TvOS: + return OsTvOS; + case ZigLLVM_WatchOS: + return OsWatchOS; + case ZigLLVM_Mesa3D: + return OsMesa3D; + case ZigLLVM_Contiki: + return OsContiki; + case ZigLLVM_AMDPAL: + return OsAMDPAL; + case ZigLLVM_HermitCore: + return OsHermitCore; + case ZigLLVM_Hurd: + return OsHurd; + case ZigLLVM_WASI: + return OsWASI; + case ZigLLVM_Emscripten: + return OsEmscripten; + } + zig_unreachable(); +} + +static void get_native_target(ZigTarget *target) { + // first zero initialize + *target = {}; + + ZigLLVM_OSType os_type; + ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os + ZigLLVMGetNativeTarget( + &target->arch, + &target->vendor, + &os_type, + &target->abi, + &oformat); + target->os = get_zig_os_type(os_type); + target->is_native = true; + if (target->abi == ZigLLVM_UnknownEnvironment) { + target->abi = target_default_abi(target->arch, target->os); + } + if (target_is_glibc(target)) { + target->glibc_version = heap::c_allocator.create(); + target_init_default_glibc_version(target); + } +} + Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu) { Error err; diff --git a/src/target.cpp b/src/target.cpp index 8a68afbd83..4df2cfca85 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -287,83 +287,6 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type) { zig_unreachable(); } -static Os get_zig_os_type(ZigLLVM_OSType os_type) { - switch (os_type) { - case ZigLLVM_UnknownOS: - return OsFreestanding; - case ZigLLVM_Ananas: - return OsAnanas; - case ZigLLVM_CloudABI: - return OsCloudABI; - case ZigLLVM_DragonFly: - return OsDragonFly; - case ZigLLVM_FreeBSD: - return OsFreeBSD; - case ZigLLVM_Fuchsia: - return OsFuchsia; - case ZigLLVM_IOS: - return OsIOS; - case ZigLLVM_KFreeBSD: - return OsKFreeBSD; - case ZigLLVM_Linux: - return OsLinux; - case ZigLLVM_Lv2: - return OsLv2; - case ZigLLVM_Darwin: - case ZigLLVM_MacOSX: - return OsMacOSX; - case ZigLLVM_NetBSD: - return OsNetBSD; - case ZigLLVM_OpenBSD: - return OsOpenBSD; - case ZigLLVM_Solaris: - return OsSolaris; - case ZigLLVM_Win32: - return OsWindows; - case ZigLLVM_Haiku: - return OsHaiku; - case ZigLLVM_Minix: - return OsMinix; - case ZigLLVM_RTEMS: - return OsRTEMS; - case ZigLLVM_NaCl: - return OsNaCl; - case ZigLLVM_CNK: - return OsCNK; - case ZigLLVM_AIX: - return OsAIX; - case ZigLLVM_CUDA: - return OsCUDA; - case ZigLLVM_NVCL: - return OsNVCL; - case ZigLLVM_AMDHSA: - return OsAMDHSA; - case ZigLLVM_PS4: - return OsPS4; - case ZigLLVM_ELFIAMCU: - return OsELFIAMCU; - case ZigLLVM_TvOS: - return OsTvOS; - case ZigLLVM_WatchOS: - return OsWatchOS; - case ZigLLVM_Mesa3D: - return OsMesa3D; - case ZigLLVM_Contiki: - return OsContiki; - case ZigLLVM_AMDPAL: - return OsAMDPAL; - case ZigLLVM_HermitCore: - return OsHermitCore; - case ZigLLVM_Hurd: - return OsHurd; - case ZigLLVM_WASI: - return OsWASI; - case ZigLLVM_Emscripten: - return OsEmscripten; - } - zig_unreachable(); -} - const char *target_os_name(Os os_type) { switch (os_type) { case OsFreestanding: @@ -447,29 +370,6 @@ Error target_parse_glibc_version(ZigGLibCVersion *glibc_ver, const char *text) { return ErrorNone; } -void get_native_target(ZigTarget *target) { - // first zero initialize - *target = {}; - - ZigLLVM_OSType os_type; - ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os - ZigLLVMGetNativeTarget( - &target->arch, - &target->vendor, - &os_type, - &target->abi, - &oformat); - target->os = get_zig_os_type(os_type); - target->is_native = true; - if (target->abi == ZigLLVM_UnknownEnvironment) { - target->abi = target_default_abi(target->arch, target->os); - } - if (target_is_glibc(target)) { - target->glibc_version = heap::c_allocator.create(); - target_init_default_glibc_version(target); - } -} - void target_init_default_glibc_version(ZigTarget *target) { *target->glibc_version = {2, 17, 0}; } diff --git a/src/target.hpp b/src/target.hpp index 9396eb2623..e72b6a6f49 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -73,7 +73,6 @@ ZigLLVM_ObjectFormatType target_oformat_enum(size_t index); const char *target_oformat_name(ZigLLVM_ObjectFormatType oformat); ZigLLVM_ObjectFormatType target_object_format(const ZigTarget *target); -void get_native_target(ZigTarget *target); void target_triple_llvm(Buf *triple, const ZigTarget *target); void target_triple_zig(Buf *triple, const ZigTarget *target); From ef24f2dd93729493531c427aaac54444597f6e66 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 02:36:16 -0500 Subject: [PATCH 62/99] remove special darwin os version min handling now it is integrated with zig's target OS range. --- lib/std/target.zig | 21 +++++- lib/std/zig/cross_target.zig | 6 +- src-self-hosted/stage2.zig | 39 +++++++---- src/all_types.hpp | 2 - src/codegen.cpp | 59 +++------------- src/codegen.hpp | 2 - src/glibc.cpp | 26 +++---- src/glibc.hpp | 2 +- src/link.cpp | 127 +++++------------------------------ src/main.cpp | 20 ------ src/stage2.cpp | 2 +- src/stage2.h | 9 ++- src/target.cpp | 4 +- src/target.hpp | 2 +- test/cli.zig | 2 +- 15 files changed, 95 insertions(+), 228 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index a72bd921b0..6ecb679f29 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -147,7 +147,6 @@ pub const Target = struct { .cloudabi, .dragonfly, .fuchsia, - .ios, .kfreebsd, .lv2, .solaris, @@ -162,8 +161,6 @@ pub const Target = struct { .amdhsa, .ps4, .elfiamcu, - .tvos, - .watchos, .mesa3d, .contiki, .amdpal, @@ -187,6 +184,24 @@ pub const Target = struct { .max = .{ .major = 10, .minor = 15, .patch = 3 }, }, }, + .ios => return .{ + .semver = .{ + .min = .{ .major = 12, .minor = 0 }, + .max = .{ .major = 13, .minor = 4, .patch = 0 }, + }, + }, + .watchos => return .{ + .semver = .{ + .min = .{ .major = 6, .minor = 0 }, + .max = .{ .major = 6, .minor = 2, .patch = 0 }, + }, + }, + .tvos => return .{ + .semver = .{ + .min = .{ .major = 13, .minor = 0 }, + .max = .{ .major = 13, .minor = 4, .patch = 0 }, + }, + }, .netbsd => return .{ .semver = .{ .min = .{ .major = 8, .minor = 0 }, diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index fac90ddba8..c7fd1f0464 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -88,7 +88,6 @@ pub const CrossTarget = struct { .cloudabi, .dragonfly, .fuchsia, - .ios, .kfreebsd, .lv2, .solaris, @@ -103,8 +102,6 @@ pub const CrossTarget = struct { .amdhsa, .ps4, .elfiamcu, - .tvos, - .watchos, .mesa3d, .contiki, .amdpal, @@ -121,8 +118,11 @@ pub const CrossTarget = struct { .freebsd, .macosx, + .ios, .netbsd, .openbsd, + .tvos, + .watchos, => { self.os_version_min = .{ .semver = os.version_range.semver.min }; self.os_version_max = .{ .semver = os.version_range.semver.max }; diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index cd3f49edfa..3dae15056e 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -892,7 +892,7 @@ const Stage2Target = extern struct { is_native: bool, - glibc_version: ?*Stage2GLibCVersion, // null means default + glibc_or_darwin_version: ?*Stage2SemVer, llvm_cpu_name: ?[*:0]const u8, llvm_cpu_features: ?[*:0]const u8, @@ -1103,16 +1103,29 @@ const Stage2Target = extern struct { os_builtin_str_buffer.toSlice()[os_builtin_str_ver_start_index..os_builtin_str_buffer.len()], ); - const glibc_version = if (target.isGnuLibC()) blk: { - const stage1_glibc = try std.heap.c_allocator.create(Stage2GLibCVersion); - const stage2_glibc = target.os.version_range.linux.glibc; - stage1_glibc.* = .{ - .major = stage2_glibc.major, - .minor = stage2_glibc.minor, - .patch = stage2_glibc.patch, - }; - break :blk stage1_glibc; - } else null; + const glibc_or_darwin_version = blk: { + if (target.isGnuLibC()) { + const stage1_glibc = try std.heap.c_allocator.create(Stage2SemVer); + const stage2_glibc = target.os.version_range.linux.glibc; + stage1_glibc.* = .{ + .major = stage2_glibc.major, + .minor = stage2_glibc.minor, + .patch = stage2_glibc.patch, + }; + break :blk stage1_glibc; + } else if (target.isDarwin()) { + const stage1_semver = try std.heap.c_allocator.create(Stage2SemVer); + const stage2_semver = target.os.version_range.semver.min; + stage1_semver.* = .{ + .major = stage2_semver.major, + .minor = stage2_semver.minor, + .patch = stage2_semver.patch, + }; + break :blk stage1_semver; + } else { + break :blk null; + } + }; self.* = .{ .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch @@ -1125,7 +1138,7 @@ const Stage2Target = extern struct { .os_builtin_str = os_builtin_str_buffer.toOwnedSlice().ptr, .cache_hash = cache_hash.toOwnedSlice().ptr, .is_native = cross_target.isNative(), - .glibc_version = glibc_version, + .glibc_or_darwin_version = glibc_or_darwin_version, .dynamic_linker = dynamic_linker, }; } @@ -1179,7 +1192,7 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) } // ABI warning -const Stage2GLibCVersion = extern struct { +const Stage2SemVer = extern struct { major: u32, minor: u32, patch: u32, diff --git a/src/all_types.hpp b/src/all_types.hpp index 7277d04359..838709b9c0 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2250,8 +2250,6 @@ struct CodeGen { bool test_is_evented; CodeModel code_model; - Buf *mmacosx_version_min; - Buf *mios_version_min; Buf *root_out_name; Buf *test_filter; Buf *test_name_prefix; diff --git a/src/codegen.cpp b/src/codegen.cpp index 15122d5e11..8ae730616d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -32,31 +32,6 @@ enum ResumeId { ResumeIdCall, }; -static void init_darwin_native(CodeGen *g) { - char *osx_target = getenv("MACOSX_DEPLOYMENT_TARGET"); - char *ios_target = getenv("IPHONEOS_DEPLOYMENT_TARGET"); - - // Allow conflicts among OSX and iOS, but choose the default platform. - if (osx_target && ios_target) { - if (g->zig_target->arch == ZigLLVM_arm || - g->zig_target->arch == ZigLLVM_aarch64 || - g->zig_target->arch == ZigLLVM_thumb) - { - osx_target = nullptr; - } else { - ios_target = nullptr; - } - } - - if (osx_target) { - g->mmacosx_version_min = buf_create_from_str(osx_target); - } else if (ios_target) { - g->mios_version_min = buf_create_from_str(ios_target); - } else if (g->zig_target->os != OsIOS) { - g->mmacosx_version_min = buf_create_from_str("10.14"); - } -} - static ZigPackage *new_package(const char *root_src_dir, const char *root_src_path, const char *pkg_path) { ZigPackage *entry = heap::c_allocator.create(); entry->package_table.init(4); @@ -160,14 +135,6 @@ void codegen_add_framework(CodeGen *g, const char *framework) { g->darwin_frameworks.append(buf_create_from_str(framework)); } -void codegen_set_mmacosx_version_min(CodeGen *g, Buf *mmacosx_version_min) { - g->mmacosx_version_min = mmacosx_version_min; -} - -void codegen_set_mios_version_min(CodeGen *g, Buf *mios_version_min) { - g->mios_version_min = mios_version_min; -} - void codegen_set_rdynamic(CodeGen *g, bool rdynamic) { g->linker_rdynamic = rdynamic; } @@ -8655,10 +8622,10 @@ static Error define_builtin_compile_vars(CodeGen *g) { if (g->zig_target->cache_hash != nullptr) { cache_str(&cache_hash, g->zig_target->cache_hash); } - if (g->zig_target->glibc_version != nullptr) { - cache_int(&cache_hash, g->zig_target->glibc_version->major); - cache_int(&cache_hash, g->zig_target->glibc_version->minor); - cache_int(&cache_hash, g->zig_target->glibc_version->patch); + if (g->zig_target->glibc_or_darwin_version != nullptr) { + cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->major); + cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->minor); + cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->patch); } cache_bool(&cache_hash, g->have_err_ret_tracing); cache_bool(&cache_hash, g->libc_link_lib != nullptr); @@ -10313,10 +10280,10 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { if (g->zig_target->cache_hash != nullptr) { cache_str(ch, g->zig_target->cache_hash); } - if (g->zig_target->glibc_version != nullptr) { - cache_int(ch, g->zig_target->glibc_version->major); - cache_int(ch, g->zig_target->glibc_version->minor); - cache_int(ch, g->zig_target->glibc_version->patch); + if (g->zig_target->glibc_or_darwin_version != nullptr) { + cache_int(ch, g->zig_target->glibc_or_darwin_version->major); + cache_int(ch, g->zig_target->glibc_or_darwin_version->minor); + cache_int(ch, g->zig_target->glibc_or_darwin_version->patch); } cache_int(ch, detect_subsystem(g)); cache_bool(ch, g->strip_debug_symbols); @@ -10344,8 +10311,6 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_bool(ch, g->emit_bin); cache_bool(ch, g->emit_llvm_ir); cache_bool(ch, g->emit_asm); - cache_buf_opt(ch, g->mmacosx_version_min); - cache_buf_opt(ch, g->mios_version_min); cache_usize(ch, g->version_major); cache_usize(ch, g->version_minor); cache_usize(ch, g->version_patch); @@ -10662,9 +10627,6 @@ CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType o codegen_set_errmsg_color(child_gen, parent_gen->err_color); - codegen_set_mmacosx_version_min(child_gen, parent_gen->mmacosx_version_min); - codegen_set_mios_version_min(child_gen, parent_gen->mios_version_min); - child_gen->enable_cache = true; return child_gen; @@ -10772,11 +10734,6 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget g->each_lib_rpath = false; } else { g->each_lib_rpath = true; - - if (target_os_is_darwin(g->zig_target->os)) { - init_darwin_native(g); - } - } if (target_os_requires_libc(g->zig_target->os)) { diff --git a/src/codegen.hpp b/src/codegen.hpp index 6329c59a5e..191da9a04b 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -35,8 +35,6 @@ LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib); void codegen_add_framework(CodeGen *codegen, const char *name); void codegen_add_rpath(CodeGen *codegen, const char *name); void codegen_set_rdynamic(CodeGen *g, bool rdynamic); -void codegen_set_mmacosx_version_min(CodeGen *g, Buf *mmacosx_version_min); -void codegen_set_mios_version_min(CodeGen *g, Buf *mios_version_min); void codegen_set_linker_script(CodeGen *g, const char *linker_script); void codegen_set_test_filter(CodeGen *g, Buf *filter); void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix); diff --git a/src/glibc.cpp b/src/glibc.cpp index 91e2f9dfc1..da5c1d5290 100644 --- a/src/glibc.cpp +++ b/src/glibc.cpp @@ -55,7 +55,7 @@ Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbo Optional> opt_component = SplitIterator_next(&it); if (!opt_component.is_some) break; Buf *ver_buf = buf_create_from_slice(opt_component.value); - ZigGLibCVersion *this_ver = glibc_abi->all_versions.add_one(); + Stage2SemVer *this_ver = glibc_abi->all_versions.add_one(); if ((err = target_parse_glibc_version(this_ver, buf_ptr(ver_buf)))) { if (verbose) { fprintf(stderr, "Unable to parse glibc version '%s': %s\n", buf_ptr(ver_buf), err_str(err)); @@ -186,9 +186,9 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con cache_buf(cache_hash, compiler_id); cache_int(cache_hash, target->arch); cache_int(cache_hash, target->abi); - cache_int(cache_hash, target->glibc_version->major); - cache_int(cache_hash, target->glibc_version->minor); - cache_int(cache_hash, target->glibc_version->patch); + cache_int(cache_hash, target->glibc_or_darwin_version->major); + cache_int(cache_hash, target->glibc_or_darwin_version->minor); + cache_int(cache_hash, target->glibc_or_darwin_version->patch); Buf digest = BUF_INIT; buf_resize(&digest, 0); @@ -224,10 +224,10 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con uint8_t target_ver_index = 0; for (;target_ver_index < glibc_abi->all_versions.length; target_ver_index += 1) { - const ZigGLibCVersion *this_ver = &glibc_abi->all_versions.at(target_ver_index); - if (this_ver->major == target->glibc_version->major && - this_ver->minor == target->glibc_version->minor && - this_ver->patch == target->glibc_version->patch) + const Stage2SemVer *this_ver = &glibc_abi->all_versions.at(target_ver_index); + if (this_ver->major == target->glibc_or_darwin_version->major && + this_ver->minor == target->glibc_or_darwin_version->minor && + this_ver->patch == target->glibc_or_darwin_version->patch) { break; } @@ -235,9 +235,9 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con if (target_ver_index == glibc_abi->all_versions.length) { if (verbose) { fprintf(stderr, "Unrecognized glibc version: %d.%d.%d\n", - target->glibc_version->major, - target->glibc_version->minor, - target->glibc_version->patch); + target->glibc_or_darwin_version->major, + target->glibc_or_darwin_version->minor, + target->glibc_or_darwin_version->patch); } return ErrorUnknownABI; } @@ -246,7 +246,7 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con Buf *map_contents = buf_alloc(); for (uint8_t ver_i = 0; ver_i < glibc_abi->all_versions.length; ver_i += 1) { - const ZigGLibCVersion *ver = &glibc_abi->all_versions.at(ver_i); + const Stage2SemVer *ver = &glibc_abi->all_versions.at(ver_i); if (ver->patch == 0) { buf_appendf(map_contents, "GLIBC_%d.%d { };\n", ver->major, ver->minor); } else { @@ -294,7 +294,7 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con uint8_t ver_index = ver_list->versions[ver_i]; Buf *stub_name; - const ZigGLibCVersion *ver = &glibc_abi->all_versions.at(ver_index); + const Stage2SemVer *ver = &glibc_abi->all_versions.at(ver_index); const char *sym_name = buf_ptr(libc_fn->name); if (ver->patch == 0) { stub_name = buf_sprintf("%s_%d_%d", sym_name, ver->major, ver->minor); diff --git a/src/glibc.hpp b/src/glibc.hpp index 8e4c7888ad..c04dcb4629 100644 --- a/src/glibc.hpp +++ b/src/glibc.hpp @@ -32,7 +32,7 @@ struct ZigGLibCAbi { Buf *abi_txt_path; Buf *vers_txt_path; Buf *fns_txt_path; - ZigList all_versions; + ZigList all_versions; ZigList all_functions; // The value is a pointer to all_functions.length items and each item is an index // into all_functions. diff --git a/src/link.cpp b/src/link.cpp index 901390feae..588771d8bf 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -2371,99 +2371,6 @@ static void construct_linker_job_coff(LinkJob *lj) { } } - -// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the -// grouped values as integers. Numbers which are not provided are set to 0. -// return true if the entire string was parsed (9.2), or all groups were -// parsed (10.3.5extrastuff). -static bool darwin_get_release_version(const char *str, int *major, int *minor, int *micro, bool *had_extra) { - *had_extra = false; - - *major = 0; - *minor = 0; - *micro = 0; - - if (*str == '\0') - return false; - - char *end; - *major = (int)strtol(str, &end, 10); - if (*str != '\0' && *end == '\0') - return true; - if (*end != '.') - return false; - - str = end + 1; - *minor = (int)strtol(str, &end, 10); - if (*str != '\0' && *end == '\0') - return true; - if (*end != '.') - return false; - - str = end + 1; - *micro = (int)strtol(str, &end, 10); - if (*str != '\0' && *end == '\0') - return true; - if (str == end) - return false; - *had_extra = true; - return true; -} - -enum DarwinPlatformKind { - MacOS, - IPhoneOS, - IPhoneOSSimulator, -}; - -struct DarwinPlatform { - DarwinPlatformKind kind; - int major; - int minor; - int micro; -}; - -static void get_darwin_platform(LinkJob *lj, DarwinPlatform *platform) { - CodeGen *g = lj->codegen; - - if (g->mmacosx_version_min) { - platform->kind = MacOS; - } else if (g->mios_version_min) { - platform->kind = IPhoneOS; - } else if (g->zig_target->os == OsMacOSX) { - platform->kind = MacOS; - g->mmacosx_version_min = buf_create_from_str("10.14"); - } else { - zig_panic("unable to infer -mmacosx-version-min or -mios-version-min"); - } - - bool had_extra; - if (platform->kind == MacOS) { - if (!darwin_get_release_version(buf_ptr(g->mmacosx_version_min), - &platform->major, &platform->minor, &platform->micro, &had_extra) || - had_extra || platform->major != 10 || platform->minor >= 100 || platform->micro >= 100) - { - zig_panic("invalid -mmacosx-version-min"); - } - } else if (platform->kind == IPhoneOS) { - if (!darwin_get_release_version(buf_ptr(g->mios_version_min), - &platform->major, &platform->minor, &platform->micro, &had_extra) || - had_extra || platform->major >= 10 || platform->minor >= 100 || platform->micro >= 100) - { - zig_panic("invalid -mios-version-min"); - } - } else { - zig_unreachable(); - } - - if (platform->kind == IPhoneOS && - (g->zig_target->arch == ZigLLVM_x86 || - g->zig_target->arch == ZigLLVM_x86_64)) - { - platform->kind = IPhoneOSSimulator; - } -} - static void construct_linker_job_macho(LinkJob *lj) { CodeGen *g = lj->codegen; @@ -2507,25 +2414,25 @@ static void construct_linker_job_macho(LinkJob *lj) { lj->args.append("-arch"); lj->args.append(get_darwin_arch_string(g->zig_target)); - DarwinPlatform platform; - get_darwin_platform(lj, &platform); - switch (platform.kind) { - case MacOS: + if (g->zig_target->glibc_or_darwin_version != nullptr) { + if (g->zig_target->os == OsMacOSX) { lj->args.append("-macosx_version_min"); - break; - case IPhoneOS: - lj->args.append("-iphoneos_version_min"); - break; - case IPhoneOSSimulator: - lj->args.append("-ios_simulator_version_min"); - break; + } else if (g->zig_target->os == OsIOS) { + if (g->zig_target->arch == ZigLLVM_x86 || g->zig_target->arch == ZigLLVM_x86_64) { + lj->args.append("-ios_simulator_version_min"); + } else { + lj->args.append("-iphoneos_version_min"); + } + } + Buf *version_string = buf_sprintf("%d.%d.%d", + g->zig_target->glibc_or_darwin_version->major, + g->zig_target->glibc_or_darwin_version->minor, + g->zig_target->glibc_or_darwin_version->patch); + lj->args.append(buf_ptr(version_string)); + + lj->args.append("-sdk_version"); + lj->args.append(buf_ptr(version_string)); } - Buf *version_string = buf_sprintf("%d.%d.%d", platform.major, platform.minor, platform.micro); - lj->args.append(buf_ptr(version_string)); - - lj->args.append("-sdk_version"); - lj->args.append(buf_ptr(version_string)); - if (g->out_type == OutTypeExe) { lj->args.append("-pie"); diff --git a/src/main.cpp b/src/main.cpp index f741cafd40..f471bda374 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -127,8 +127,6 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --subsystem [subsystem] (windows) /SUBSYSTEM: to the linker\n" " -F[dir] (darwin) add search path for frameworks\n" " -framework [name] (darwin) link against framework\n" - " -mios-version-min [ver] (darwin) set iOS deployment target\n" - " -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target\n" " --ver-major [ver] dynamic library semver major version\n" " --ver-minor [ver] dynamic library semver minor version\n" " --ver-patch [ver] dynamic library semver patch version\n" @@ -414,8 +412,6 @@ static int main0(int argc, char **argv) { bool have_libc = false; const char *target_string = nullptr; bool rdynamic = false; - const char *mmacosx_version_min = nullptr; - const char *mios_version_min = nullptr; const char *linker_script = nullptr; Buf *version_script = nullptr; ZigList rpath_list = {0}; @@ -844,10 +840,6 @@ static int main0(int argc, char **argv) { cache_dir = argv[i]; } else if (strcmp(arg, "-target") == 0) { target_string = argv[i]; - } else if (strcmp(arg, "-mmacosx-version-min") == 0) { - mmacosx_version_min = argv[i]; - } else if (strcmp(arg, "-mios-version-min") == 0) { - mios_version_min = argv[i]; } else if (strcmp(arg, "-framework") == 0) { frameworks.append(argv[i]); } else if (strcmp(arg, "--linker-script") == 0) { @@ -1240,18 +1232,6 @@ static int main0(int argc, char **argv) { } codegen_set_rdynamic(g, rdynamic); - if (mmacosx_version_min && mios_version_min) { - fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n"); - return main_exit(root_progress_node, EXIT_FAILURE); - } - - if (mmacosx_version_min) { - codegen_set_mmacosx_version_min(g, buf_create_from_str(mmacosx_version_min)); - } - - if (mios_version_min) { - codegen_set_mios_version_min(g, buf_create_from_str(mios_version_min)); - } if (test_filter) { codegen_set_test_filter(g, buf_create_from_str(test_filter)); diff --git a/src/stage2.cpp b/src/stage2.cpp index bc8e65fd56..67a518e1fa 100644 --- a/src/stage2.cpp +++ b/src/stage2.cpp @@ -186,7 +186,7 @@ static void get_native_target(ZigTarget *target) { target->abi = target_default_abi(target->arch, target->os); } if (target_is_glibc(target)) { - target->glibc_version = heap::c_allocator.create(); + target->glibc_or_darwin_version = heap::c_allocator.create(); target_init_default_glibc_version(target); } } diff --git a/src/stage2.h b/src/stage2.h index 96222e3138..20311788b2 100644 --- a/src/stage2.h +++ b/src/stage2.h @@ -270,14 +270,12 @@ enum Os { }; // ABI warning -struct ZigGLibCVersion { - uint32_t major; // always 2 +struct Stage2SemVer { + uint32_t major; uint32_t minor; uint32_t patch; }; -struct Stage2TargetData; - // ABI warning struct ZigTarget { enum ZigLLVM_ArchType arch; @@ -288,7 +286,8 @@ struct ZigTarget { bool is_native; - struct ZigGLibCVersion *glibc_version; // null means default + // null means default. this is double-purposed to be darwin min version + struct Stage2SemVer *glibc_or_darwin_version; const char *llvm_cpu_name; const char *llvm_cpu_features; diff --git a/src/target.cpp b/src/target.cpp index 4df2cfca85..96ac2e1666 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -347,7 +347,7 @@ const char *target_abi_name(ZigLLVM_EnvironmentType abi) { return ZigLLVMGetEnvironmentTypeName(abi); } -Error target_parse_glibc_version(ZigGLibCVersion *glibc_ver, const char *text) { +Error target_parse_glibc_version(Stage2SemVer *glibc_ver, const char *text) { glibc_ver->major = 2; glibc_ver->minor = 0; glibc_ver->patch = 0; @@ -371,7 +371,7 @@ Error target_parse_glibc_version(ZigGLibCVersion *glibc_ver, const char *text) { } void target_init_default_glibc_version(ZigTarget *target) { - *target->glibc_version = {2, 17, 0}; + *target->glibc_or_darwin_version = {2, 17, 0}; } Error target_parse_arch(ZigLLVM_ArchType *out_arch, const char *arch_ptr, size_t arch_len) { diff --git a/src/target.hpp b/src/target.hpp index e72b6a6f49..9c6e8ce46e 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -46,7 +46,7 @@ Error target_parse_arch(ZigLLVM_ArchType *arch, const char *arch_ptr, size_t arc Error target_parse_os(Os *os, const char *os_ptr, size_t os_len); Error target_parse_abi(ZigLLVM_EnvironmentType *abi, const char *abi_ptr, size_t abi_len); -Error target_parse_glibc_version(ZigGLibCVersion *out, const char *text); +Error target_parse_glibc_version(Stage2SemVer *out, const char *text); void target_init_default_glibc_version(ZigTarget *target); size_t target_arch_count(void); diff --git a/test/cli.zig b/test/cli.zig index bc5a29be44..117c714a29 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -92,7 +92,7 @@ fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void { fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" }); const run_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "run" }); - testing.expect(std.mem.eql(u8, run_result.stderr, "All your base are belong to us.\n")); + testing.expect(std.mem.eql(u8, run_result.stderr, "All your codebase are belong to us.\n")); } fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { From 07f52119de2a8bdb84389c73332e113cf12ac997 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 03:11:37 -0500 Subject: [PATCH 63/99] implement native OS version detection for linux --- lib/std/c.zig | 1 + lib/std/os.zig | 24 +++++++++++++----------- lib/std/zig/system.zig | 17 ++++++++++++++++- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 9d7d9524d6..48a3039f51 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -125,6 +125,7 @@ pub extern "c" fn sysctlnametomib(name: [*:0]const u8, mibp: ?*c_int, sizep: ?*u pub extern "c" fn tcgetattr(fd: fd_t, termios_p: *termios) c_int; pub extern "c" fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) c_int; pub extern "c" fn fcntl(fd: fd_t, cmd: c_int, ...) c_int; +pub extern "c" fn uname(buf: *utsname) c_int; pub extern "c" fn gethostname(name: [*]u8, len: usize) c_int; pub extern "c" fn bind(socket: fd_t, address: ?*const sockaddr, address_len: socklen_t) c_int; diff --git a/lib/std/os.zig b/lib/std/os.zig index 9f349e7dc4..49e88bf9c7 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3295,22 +3295,24 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 { } } if (builtin.os.tag == .linux) { - var uts: utsname = undefined; - switch (errno(system.uname(&uts))) { - 0 => { - const hostname = mem.toSlice(u8, @ptrCast([*:0]u8, &uts.nodename)); - mem.copy(u8, name_buffer, hostname); - return name_buffer[0..hostname.len]; - }, - EFAULT => unreachable, - EPERM => return error.PermissionDenied, - else => |err| return unexpectedErrno(err), - } + const uts = uname(); + const hostname = mem.toSliceConst(u8, @ptrCast([*:0]const u8, &uts.nodename)); + mem.copy(u8, name_buffer, hostname); + return name_buffer[0..hostname.len]; } @compileError("TODO implement gethostname for this OS"); } +pub fn uname() utsname { + var uts: utsname = undefined; + switch (errno(system.uname(&uts))) { + 0 => return uts, + EFAULT => unreachable, + else => unreachable, + } +} + pub fn res_mkquery( op: u4, dname: []const u8, diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index fe2f4c7e04..ffae5c6015 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -200,7 +200,22 @@ pub const NativeTargetInfo = struct { const cpu = Target.Cpu.baseline(arch); // TODO Detect native operating system version. Until that is implemented we use the default range. - const os = Target.Os.defaultVersionRange(os_tag); + var os = Target.Os.defaultVersionRange(os_tag); + switch (Target.current.os.tag) { + .linux => { + const uts = std.os.uname(); + const release = mem.toSliceConst(u8, @ptrCast([*:0]const u8, &uts.release)); + if (std.builtin.Version.parse(release)) |ver| { + os.version_range.linux.range.min = ver; + os.version_range.linux.range.max = ver; + } else |err| switch (err) { + error.Overflow => {}, + error.InvalidCharacter => {}, + error.InvalidVersion => {}, + } + }, + else => {}, + } return detectAbiAndDynamicLinker(allocator, cpu, os); } From 500dde32d5cf59f5700fb3f69ae6c7a0defd8a93 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 13:27:52 -0500 Subject: [PATCH 64/99] dynamic_linker becomes a field of std.zig.CrossTarget --- lib/std/build.zig | 12 ++--- lib/std/target.zig | 92 +++++++++++++++++++++++++----------- lib/std/zig/cross_target.zig | 17 +++++-- lib/std/zig/system.zig | 77 ++++++++++-------------------- src-self-hosted/stage2.zig | 15 +++--- src/all_types.hpp | 1 - src/codegen.cpp | 19 ++------ src/link.cpp | 4 +- src/main.cpp | 11 ++--- src/stage2.cpp | 7 ++- src/stage2.h | 3 +- src/target.cpp | 4 +- src/target.hpp | 2 +- 13 files changed, 137 insertions(+), 127 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 7852828f7c..3ca59ae4cb 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1177,8 +1177,6 @@ pub const LibExeObjStep = struct { /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`. glibc_multi_install_dir: ?[]const u8 = null, - dynamic_linker: ?[]const u8 = null, - /// Position Independent Code force_pic: ?bool = null, @@ -1978,6 +1976,11 @@ pub const LibExeObjStep = struct { } try zig_args.append(mcpu_buffer.toSliceConst()); } + + if (self.target.dynamic_linker.get()) |dynamic_linker| { + try zig_args.append("--dynamic-linker"); + try zig_args.append(dynamic_linker); + } } if (self.linker_script) |linker_script| { @@ -1985,11 +1988,6 @@ pub const LibExeObjStep = struct { zig_args.append(builder.pathFromRoot(linker_script)) catch unreachable; } - if (self.dynamic_linker) |dynamic_linker| { - try zig_args.append("--dynamic-linker"); - try zig_args.append(dynamic_linker); - } - if (self.version_script) |version_script| { try zig_args.append("--version-script"); try zig_args.append(builder.pathFromRoot(version_script)); diff --git a/lib/std/target.zig b/lib/std/target.zig index 6ecb679f29..3054c60467 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1099,16 +1099,52 @@ pub const Target = struct { } } + pub const DynamicLinker = struct { + /// Contains the memory used to store the dynamic linker path. This field should + /// not be used directly. See `get` and `set`. This field exists so that this API requires no allocator. + buffer: [255]u8 = undefined, + + /// Used to construct the dynamic linker path. This field should not be used + /// directly. See `get` and `set`. + max_byte: ?u8 = null, + + /// Asserts that the length is less than or equal to 255 bytes. + pub fn init(dl_or_null: ?[]const u8) DynamicLinker { + var result: DynamicLinker = undefined; + result.set(dl_or_null); + return result; + } + + /// The returned memory has the same lifetime as the `DynamicLinker`. + pub fn get(self: *const DynamicLinker) ?[]const u8 { + const m: usize = self.max_byte orelse return null; + return self.buffer[0 .. m + 1]; + } + + /// Asserts that the length is less than or equal to 255 bytes. + pub fn set(self: *DynamicLinker, dl_or_null: ?[]const u8) void { + if (dl_or_null) |dl| { + mem.copy(u8, &self.buffer, dl); + self.max_byte = @intCast(u8, dl.len - 1); + } else { + self.max_byte = null; + } + } + }; + /// The result will be a byte index *pointing at the final byte*. In other words, length minus one. /// A return value of `null` means the concept of a dynamic linker is not meaningful for that target. - pub fn standardDynamicLinkerPath(self: Target, buffer: *[255]u8) ?u8 { + pub fn standardDynamicLinkerPath(self: Target) DynamicLinker { + var result: DynamicLinker = .{}; const S = struct { - fn print(b: *[255]u8, comptime fmt: []const u8, args: var) u8 { - return @intCast(u8, (std.fmt.bufPrint(b, fmt, args) catch unreachable).len - 1); + fn print(r: *DynamicLinker, comptime fmt: []const u8, args: var) DynamicLinker { + r.max_byte = @intCast(u8, (std.fmt.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1); + return r.*; } - fn copy(b: *[255]u8, s: []const u8) u8 { - mem.copy(u8, b, s); - return @intCast(u8, s.len - 1); + fn copy(r: *DynamicLinker, s: []const u8) DynamicLinker { + mem.copy(u8, &r.buffer, s); + r.max_byte = @intCast(u8, s.len - 1); + return r.*; } }; const print = S.print; @@ -1116,7 +1152,7 @@ pub const Target = struct { if (self.isAndroid()) { const suffix = if (self.cpu.arch.ptrBitWidth() == 64) "64" else ""; - return print(buffer, "/system/bin/linker{}", .{suffix}); + return print(&result, "/system/bin/linker{}", .{suffix}); } if (self.isMusl()) { @@ -1130,28 +1166,28 @@ pub const Target = struct { else => |arch| @tagName(arch), }; const arch_suffix = if (is_arm and self.getFloatAbi() == .hard) "hf" else ""; - return print(buffer, "/lib/ld-musl-{}{}.so.1", .{ arch_part, arch_suffix }); + return print(&result, "/lib/ld-musl-{}{}.so.1", .{ arch_part, arch_suffix }); } switch (self.os.tag) { - .freebsd => return copy(buffer, "/libexec/ld-elf.so.1"), - .netbsd => return copy(buffer, "/libexec/ld.elf_so"), - .dragonfly => return copy(buffer, "/libexec/ld-elf.so.2"), + .freebsd => return copy(&result, "/libexec/ld-elf.so.1"), + .netbsd => return copy(&result, "/libexec/ld.elf_so"), + .dragonfly => return copy(&result, "/libexec/ld-elf.so.2"), .linux => switch (self.cpu.arch) { .i386, .sparc, .sparcel, - => return copy(buffer, "/lib/ld-linux.so.2"), + => return copy(&result, "/lib/ld-linux.so.2"), - .aarch64 => return copy(buffer, "/lib/ld-linux-aarch64.so.1"), - .aarch64_be => return copy(buffer, "/lib/ld-linux-aarch64_be.so.1"), - .aarch64_32 => return copy(buffer, "/lib/ld-linux-aarch64_32.so.1"), + .aarch64 => return copy(&result, "/lib/ld-linux-aarch64.so.1"), + .aarch64_be => return copy(&result, "/lib/ld-linux-aarch64_be.so.1"), + .aarch64_32 => return copy(&result, "/lib/ld-linux-aarch64_32.so.1"), .arm, .armeb, .thumb, .thumbeb, - => return copy(buffer, switch (self.getFloatAbi()) { + => return copy(&result, switch (self.getFloatAbi()) { .hard => "/lib/ld-linux-armhf.so.3", else => "/lib/ld-linux.so.3", }), @@ -1168,20 +1204,20 @@ pub const Target = struct { }; const is_nan_2008 = mips.featureSetHas(self.cpu.features, .nan2008); const loader = if (is_nan_2008) "ld-linux-mipsn8.so.1" else "ld.so.1"; - return print(buffer, "/lib{}/{}", .{ lib_suffix, loader }); + return print(&result, "/lib{}/{}", .{ lib_suffix, loader }); }, - .powerpc => return copy(buffer, "/lib/ld.so.1"), - .powerpc64, .powerpc64le => return copy(buffer, "/lib64/ld64.so.2"), - .s390x => return copy(buffer, "/lib64/ld64.so.1"), - .sparcv9 => return copy(buffer, "/lib64/ld-linux.so.2"), - .x86_64 => return copy(buffer, switch (self.abi) { + .powerpc => return copy(&result, "/lib/ld.so.1"), + .powerpc64, .powerpc64le => return copy(&result, "/lib64/ld64.so.2"), + .s390x => return copy(&result, "/lib64/ld64.so.1"), + .sparcv9 => return copy(&result, "/lib64/ld-linux.so.2"), + .x86_64 => return copy(&result, switch (self.abi) { .gnux32 => "/libx32/ld-linux-x32.so.2", else => "/lib64/ld-linux-x86-64.so.2", }), - .riscv32 => return copy(buffer, "/lib/ld-linux-riscv32-ilp32.so.1"), - .riscv64 => return copy(buffer, "/lib/ld-linux-riscv64-lp64.so.1"), + .riscv32 => return copy(&result, "/lib/ld-linux-riscv32-ilp32.so.1"), + .riscv64 => return copy(&result, "/lib/ld-linux-riscv64-lp64.so.1"), // Architectures in this list have been verified as not having a standard // dynamic linker path. @@ -1191,7 +1227,7 @@ pub const Target = struct { .bpfeb, .nvptx, .nvptx64, - => return null, + => return result, // TODO go over each item in this list and either move it to the above list, or // implement the standard dynamic linker path code for it. @@ -1217,7 +1253,7 @@ pub const Target = struct { .lanai, .renderscript32, .renderscript64, - => return null, + => return result, }, // Operating systems in this list have been verified as not having a standard @@ -1232,7 +1268,7 @@ pub const Target = struct { .emscripten, .wasi, .other, - => return null, + => return result, // TODO go over each item in this list and either move it to the above list, or // implement the standard dynamic linker path code for it. @@ -1259,7 +1295,7 @@ pub const Target = struct { .amdpal, .hermit, .hurd, - => return null, + => return result, } } }; diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index c7fd1f0464..212bc8eb9a 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -40,6 +40,10 @@ pub const CrossTarget = struct { /// If `isGnuLibC()` is `false`, this must be `null` and is ignored. glibc_version: ?SemVer = null, + /// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path + /// based on the `os_tag`. + dynamic_linker: DynamicLinker = DynamicLinker{}, + pub const OsVersion = union(enum) { none: void, semver: SemVer, @@ -48,6 +52,8 @@ pub const CrossTarget = struct { pub const SemVer = std.builtin.Version; + pub const DynamicLinker = Target.DynamicLinker; + pub fn fromTarget(target: Target) CrossTarget { var result: CrossTarget = .{ .cpu_arch = target.cpu.arch, @@ -170,6 +176,10 @@ pub const CrossTarget = struct { /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline". cpu_features: ?[]const u8 = null, + /// Absolute path to dynamic linker, to override the default, which is either a natively + /// detected path, or a standard path. + dynamic_linker: ?[]const u8 = null, + /// If this is provided, the function will populate some information about parsing failures, /// so that user-friendly error messages can be delivered. diagnostics: ?*Diagnostics = null, @@ -199,8 +209,9 @@ pub const CrossTarget = struct { var dummy_diags: ParseOptions.Diagnostics = undefined; const diags = args.diagnostics orelse &dummy_diags; - // Start with everything initialized to default values. - var result: CrossTarget = .{}; + var result: CrossTarget = .{ + .dynamic_linker = DynamicLinker.init(args.dynamic_linker), + }; var it = mem.separate(args.arch_os_abi, "-"); const arch_name = it.next().?; @@ -446,7 +457,7 @@ pub const CrossTarget = struct { return self.cpu_arch == null and self.cpu_model == null and self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty() and self.os_tag == null and self.os_version_min == null and self.os_version_max == null and - self.abi == null; + self.abi == null and self.dynamic_linker.get() == null; } pub fn zigTriple(self: CrossTarget, allocator: *mem.Allocator) error{OutOfMemory}![:0]u8 { diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index ffae5c6015..44ff9af674 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -168,14 +168,9 @@ pub const NativePaths = struct { pub const NativeTargetInfo = struct { target: Target, - /// Contains the memory used to store the dynamic linker path. This field should - /// not be used directly. See `dynamicLinker` and `setDynamicLinker`. This field - /// exists so that this API requires no allocator. - dynamic_linker_buffer: [255]u8 = undefined, + dynamic_linker: DynamicLinker = DynamicLinker{}, - /// Used to construct the dynamic linker path. This field should not be used - /// directly. See `dynamicLinker` and `setDynamicLinker`. - dynamic_linker_max: ?u8 = null, + pub const DynamicLinker = Target.DynamicLinker; pub const DetectError = error{ OutOfMemory, @@ -220,21 +215,6 @@ pub const NativeTargetInfo = struct { return detectAbiAndDynamicLinker(allocator, cpu, os); } - /// The returned memory has the same lifetime as the `NativeTargetInfo`. - pub fn dynamicLinker(self: *const NativeTargetInfo) ?[]const u8 { - const m: usize = self.dynamic_linker_max orelse return null; - return self.dynamic_linker_buffer[0 .. m + 1]; - } - - pub fn setDynamicLinker(self: *NativeTargetInfo, dl_or_null: ?[]const u8) void { - if (dl_or_null) |dl| { - mem.copy(u8, &self.dynamic_linker_buffer, dl); - self.dynamic_linker_max = @intCast(u8, dl.len - 1); - } else { - self.dynamic_linker_max = null; - } - } - /// First we attempt to use the executable's own binary. If it is dynamically /// linked, then it should answer both the C ABI question and the dynamic linker question. /// If it is statically linked, then we try /usr/bin/env. If that does not provide the answer, then @@ -273,15 +253,14 @@ pub const NativeTargetInfo = struct { .os = os, .abi = abi, }; - const ld_info = &ld_info_list_buffer[ld_info_list_len]; - ld_info_list_len += 1; + const ld = target.standardDynamicLinkerPath(); + if (ld.get() == null) continue; - ld_info.* = .{ - .ld_path_buffer = undefined, - .ld_path_max = undefined, + ld_info_list_buffer[ld_info_list_len] = .{ + .ld = ld, .abi = abi, }; - ld_info.ld_path_max = target.standardDynamicLinkerPath(&ld_info.ld_path_buffer) orelse continue; + ld_info_list_len += 1; } const ld_info_list = ld_info_list_buffer[0..ld_info_list_len]; @@ -298,7 +277,7 @@ pub const NativeTargetInfo = struct { // This is O(N^M) but typical case here is N=2 and M=10. find_ld: for (lib_paths) |lib_path| { for (ld_info_list) |ld_info| { - const standard_ld_basename = fs.path.basename(ld_info.ldPath()); + const standard_ld_basename = fs.path.basename(ld_info.ld.get().?); if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) { found_ld_info = ld_info; found_ld_path = lib_path; @@ -329,8 +308,8 @@ pub const NativeTargetInfo = struct { .os = os_adjusted, .abi = found_ld_info.abi, }, + .dynamic_linker = DynamicLinker.init(found_ld_path), }; - result.setDynamicLinker(found_ld_path); return result; } @@ -472,18 +451,18 @@ pub const NativeTargetInfo = struct { elf.PT_INTERP => { const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); - if (p_filesz > result.dynamic_linker_buffer.len) return error.NameTooLong; - _ = try preadFull(env_file, result.dynamic_linker_buffer[0..p_filesz], p_offset, p_filesz); + if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong; + _ = try preadFull(env_file, result.dynamic_linker.buffer[0..p_filesz], p_offset, p_filesz); // PT_INTERP includes a null byte in p_filesz. const len = p_filesz - 1; - // dynamic_linker_max is "max", not "len". - // We know it will fit in u8 because we check against dynamic_linker_buffer.len above. - result.dynamic_linker_max = @intCast(u8, len - 1); + // dynamic_linker.max_byte is "max", not "len". + // We know it will fit in u8 because we check against dynamic_linker.buffer.len above. + result.dynamic_linker.max_byte = @intCast(u8, len - 1); // Use it to determine ABI. - const full_ld_path = result.dynamic_linker_buffer[0..len]; + const full_ld_path = result.dynamic_linker.buffer[0..len]; for (ld_info_list) |ld_info| { - const standard_ld_basename = fs.path.basename(ld_info.ldPath()); + const standard_ld_basename = fs.path.basename(ld_info.ld.get().?); if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) { result.target.abi = ld_info.abi; break; @@ -679,26 +658,20 @@ pub const NativeTargetInfo = struct { } fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { - var result: NativeTargetInfo = .{ - .target = .{ - .cpu = cpu, - .os = os, - .abi = Target.Abi.default(cpu.arch, os), - }, + const target: Target = .{ + .cpu = cpu, + .os = os, + .abi = Target.Abi.default(cpu.arch, os), + }; + return NativeTargetInfo{ + .target = target, + .dynamic_linker = target.standardDynamicLinkerPath(), }; - result.dynamic_linker_max = result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer); - return result; } const LdInfo = struct { - ld_path_buffer: [255]u8, - ld_path_max: u8, + ld: DynamicLinker, abi: Target.Abi, - - pub fn ldPath(self: *const LdInfo) []const u8 { - const m: usize = self.ld_path_max; - return self.ld_path_buffer[0 .. m + 1]; - } }; fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) { diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 3dae15056e..726c65937e 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -651,8 +651,9 @@ export fn stage2_target_parse( target: *Stage2Target, zig_triple: ?[*:0]const u8, mcpu: ?[*:0]const u8, + dynamic_linker: ?[*:0]const u8, ) Error { - stage2TargetParse(target, zig_triple, mcpu) catch |err| switch (err) { + stage2TargetParse(target, zig_triple, mcpu, dynamic_linker) catch |err| switch (err) { error.OutOfMemory => return .OutOfMemory, error.UnknownArchitecture => return .UnknownArchitecture, error.UnknownOperatingSystem => return .UnknownOperatingSystem, @@ -676,14 +677,17 @@ fn stage2TargetParse( stage1_target: *Stage2Target, zig_triple_oz: ?[*:0]const u8, mcpu_oz: ?[*:0]const u8, + dynamic_linker_oz: ?[*:0]const u8, ) !void { const target: CrossTarget = if (zig_triple_oz) |zig_triple_z| blk: { const zig_triple = mem.toSliceConst(u8, zig_triple_z); const mcpu = if (mcpu_oz) |mcpu_z| mem.toSliceConst(u8, mcpu_z) else null; + const dynamic_linker = if (dynamic_linker_oz) |dl_z| mem.toSliceConst(u8, dl_z) else null; var diags: CrossTarget.ParseOptions.Diagnostics = .{}; break :blk CrossTarget.parse(.{ .arch_os_abi = zig_triple, .cpu_features = mcpu, + .dynamic_linker = dynamic_linker, .diagnostics = &diags, }) catch |err| switch (err) { error.UnknownCpuModel => { @@ -1170,7 +1174,7 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) if (cross_target.os_tag == null) { adjusted_target.os = detected_info.target.os; - if (detected_info.dynamicLinker()) |dl| { + if (detected_info.dynamic_linker.get()) |dl| { have_native_dl = true; dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl); } @@ -1182,11 +1186,8 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) } } if (!have_native_dl) { - var buf: [255]u8 = undefined; - dynamic_linker_ptr.* = if (adjusted_target.standardDynamicLinkerPath(&buf)) |m| - try mem.dupeZ(std.heap.c_allocator, u8, buf[0 .. @as(usize, m) + 1]) - else - null; + const dl = adjusted_target.standardDynamicLinkerPath(); + dynamic_linker_ptr.* = if (dl.get()) |s| try mem.dupeZ(std.heap.c_allocator, u8, s) else null; } return adjusted_target; } diff --git a/src/all_types.hpp b/src/all_types.hpp index 838709b9c0..9f6bfd80a5 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2255,7 +2255,6 @@ struct CodeGen { Buf *test_name_prefix; Buf *zig_lib_dir; Buf *zig_std_dir; - Buf *dynamic_linker_path; Buf *version_script_path; const char **llvm_argv; diff --git a/src/codegen.cpp b/src/codegen.cpp index 8ae730616d..62f2c7a8ba 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8832,19 +8832,6 @@ static void init(CodeGen *g) { } } -static void detect_dynamic_linker(CodeGen *g) { - if (g->dynamic_linker_path != nullptr) - return; - if (!g->have_dynamic_link) - return; - if (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic)) - return; - - if (g->zig_target->dynamic_linker != nullptr) { - g->dynamic_linker_path = buf_create_from_str(g->zig_target->dynamic_linker); - } -} - static void detect_libc(CodeGen *g) { Error err; @@ -10285,6 +10272,9 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_int(ch, g->zig_target->glibc_or_darwin_version->minor); cache_int(ch, g->zig_target->glibc_or_darwin_version->patch); } + if (g->zig_target->dynamic_linker != nullptr) { + cache_str(ch, g->zig_target->dynamic_linker); + } cache_int(ch, detect_subsystem(g)); cache_bool(ch, g->strip_debug_symbols); cache_bool(ch, g->is_test_build); @@ -10325,7 +10315,6 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_str(ch, g->libc->msvc_lib_dir); cache_str(ch, g->libc->kernel32_lib_dir); } - cache_buf_opt(ch, g->dynamic_linker_path); cache_buf_opt(ch, g->version_script_path); // gen_c_objects appends objects to g->link_objects which we want to include in the hash @@ -10422,7 +10411,6 @@ void codegen_build_and_link(CodeGen *g) { g->have_err_ret_tracing = detect_err_ret_tracing(g); g->have_sanitize_c = detect_sanitize_c(g); detect_libc(g); - detect_dynamic_linker(g); Buf digest = BUF_INIT; if (g->enable_cache) { @@ -10619,7 +10607,6 @@ CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType o child_gen->verbose_cc = parent_gen->verbose_cc; child_gen->verbose_llvm_cpu_features = parent_gen->verbose_llvm_cpu_features; child_gen->llvm_argv = parent_gen->llvm_argv; - child_gen->dynamic_linker_path = parent_gen->dynamic_linker_path; codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); child_gen->want_pic = parent_gen->have_pic ? WantPICEnabled : WantPICDisabled; diff --git a/src/link.cpp b/src/link.cpp index 588771d8bf..03534db3b2 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -1751,9 +1751,9 @@ static void construct_linker_job_elf(LinkJob *lj) { } if (g->have_dynamic_link && (is_dyn_lib || g->out_type == OutTypeExe)) { - assert(g->dynamic_linker_path != nullptr); + assert(g->zig_target->dynamic_linker != nullptr); lj->args.append("-dynamic-linker"); - lj->args.append(buf_ptr(g->dynamic_linker_path)); + lj->args.append(g->zig_target->dynamic_linker); } } diff --git a/src/main.cpp b/src/main.cpp index f471bda374..92d08bed4f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -401,7 +401,7 @@ static int main0(int argc, char **argv) { bool link_eh_frame_hdr = false; ErrColor color = ErrColorAuto; CacheOpt enable_cache = CacheOptAuto; - Buf *dynamic_linker = nullptr; + const char *dynamic_linker = nullptr; const char *libc_txt = nullptr; ZigList clang_argv = {0}; ZigList lib_dirs = {0}; @@ -496,7 +496,7 @@ static int main0(int argc, char **argv) { os_path_join(get_zig_special_dir(zig_lib_dir), buf_create_from_str("build_runner.zig"), build_runner_path); ZigTarget target; - if ((err = target_parse_triple(&target, "native", nullptr))) { + if ((err = target_parse_triple(&target, "native", nullptr, nullptr))) { fprintf(stderr, "Unable to get native target: %s\n", err_str(err)); return EXIT_FAILURE; } @@ -766,7 +766,7 @@ static int main0(int argc, char **argv) { } else if (strcmp(arg, "--name") == 0) { out_name = argv[i]; } else if (strcmp(arg, "--dynamic-linker") == 0) { - dynamic_linker = buf_create_from_str(argv[i]); + dynamic_linker = argv[i]; } else if (strcmp(arg, "--libc") == 0) { libc_txt = argv[i]; } else if (strcmp(arg, "-D") == 0) { @@ -968,7 +968,7 @@ static int main0(int argc, char **argv) { init_all_targets(); ZigTarget target; - if ((err = target_parse_triple(&target, target_string, mcpu))) { + if ((err = target_parse_triple(&target, target_string, mcpu, dynamic_linker))) { fprintf(stderr, "invalid target: %s\n" "See `%s targets` to display valid targets.\n", err_str(err), arg0); return print_error_usage(arg0); @@ -1193,7 +1193,6 @@ static int main0(int argc, char **argv) { codegen_set_strip(g, strip); g->is_dynamic = is_dynamic; - g->dynamic_linker_path = dynamic_linker; g->verbose_tokenize = verbose_tokenize; g->verbose_ast = verbose_ast; g->verbose_link = verbose_link; @@ -1320,7 +1319,7 @@ static int main0(int argc, char **argv) { return main_exit(root_progress_node, EXIT_SUCCESS); } else if (cmd == CmdTest) { ZigTarget native; - if ((err = target_parse_triple(&native, "native", nullptr))) { + if ((err = target_parse_triple(&native, "native", nullptr, nullptr))) { fprintf(stderr, "Unable to get native target: %s\n", err_str(err)); return EXIT_FAILURE; } diff --git a/src/stage2.cpp b/src/stage2.cpp index 67a518e1fa..1f6cb2d6aa 100644 --- a/src/stage2.cpp +++ b/src/stage2.cpp @@ -191,7 +191,9 @@ static void get_native_target(ZigTarget *target) { } } -Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu) { +Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu, + const char *dynamic_linker) +{ Error err; if (zig_triple == nullptr) { @@ -249,6 +251,9 @@ Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, cons target->cache_hash = "\n\n"; } + if (dynamic_linker != nullptr) { + target->dynamic_linker = dynamic_linker; + } return ErrorNone; } diff --git a/src/stage2.h b/src/stage2.h index 20311788b2..b73269f4e8 100644 --- a/src/stage2.h +++ b/src/stage2.h @@ -298,7 +298,8 @@ struct ZigTarget { }; // ABI warning -ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu); +ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu, + const char *dynamic_linker); // ABI warning diff --git a/src/target.cpp b/src/target.cpp index 96ac2e1666..f0030e05ac 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -410,8 +410,8 @@ Error target_parse_abi(ZigLLVM_EnvironmentType *out_abi, const char *abi_ptr, si return ErrorUnknownABI; } -Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu) { - return stage2_target_parse(target, triple, mcpu); +Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu, const char *dynamic_linker) { + return stage2_target_parse(target, triple, mcpu, dynamic_linker); } const char *target_arch_name(ZigLLVM_ArchType arch) { diff --git a/src/target.hpp b/src/target.hpp index 9c6e8ce46e..01f2c6b168 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -41,7 +41,7 @@ enum CIntType { CIntTypeCount, }; -Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu); +Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu, const char *dynamic_linker); Error target_parse_arch(ZigLLVM_ArchType *arch, const char *arch_ptr, size_t arch_len); Error target_parse_os(Os *os, const char *os_ptr, size_t os_len); Error target_parse_abi(ZigLLVM_EnvironmentType *abi, const char *abi_ptr, size_t abi_len); From 578dc16910c7fb6c9e3a059915b13fa6609e066f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 15:41:30 -0500 Subject: [PATCH 65/99] fix compiler crash when comptime parsing targets --- src/analyze.cpp | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 2c8244d053..90037a1191 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5401,6 +5401,8 @@ bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) { static bool can_mutate_comptime_var_state(ZigValue *value) { assert(value != nullptr); + if (value->special == ConstValSpecialUndef) + return false; switch (value->type->id) { case ZigTypeIdInvalid: zig_unreachable(); @@ -6701,8 +6703,16 @@ bool const_values_equal_ptr(ZigValue *a, ZigValue *b) { } static bool const_values_equal_array(CodeGen *g, ZigValue *a, ZigValue *b, size_t len) { - assert(a->data.x_array.special != ConstArraySpecialUndef); - assert(b->data.x_array.special != ConstArraySpecialUndef); + if (a->data.x_array.special == ConstArraySpecialUndef && + b->data.x_array.special == ConstArraySpecialUndef) + { + return true; + } + if (a->data.x_array.special == ConstArraySpecialUndef || + b->data.x_array.special == ConstArraySpecialUndef) + { + return false; + } if (a->data.x_array.special == ConstArraySpecialBuf && b->data.x_array.special == ConstArraySpecialBuf) { @@ -9390,13 +9400,24 @@ void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src) { dest->data.x_struct.fields[i]->parent.data.p_struct.field_index = i; } } else if (dest->type->id == ZigTypeIdArray) { - if (dest->data.x_array.special == ConstArraySpecialNone) { - dest->data.x_array.data.s_none.elements = g->pass1_arena->allocate(dest->type->data.array.len); - for (uint64_t i = 0; i < dest->type->data.array.len; i += 1) { - copy_const_val(g, &dest->data.x_array.data.s_none.elements[i], &src->data.x_array.data.s_none.elements[i]); - dest->data.x_array.data.s_none.elements[i].parent.id = ConstParentIdArray; - dest->data.x_array.data.s_none.elements[i].parent.data.p_array.array_val = dest; - dest->data.x_array.data.s_none.elements[i].parent.data.p_array.elem_index = i; + switch (dest->data.x_array.special) { + case ConstArraySpecialNone: { + dest->data.x_array.data.s_none.elements = g->pass1_arena->allocate(dest->type->data.array.len); + for (uint64_t i = 0; i < dest->type->data.array.len; i += 1) { + copy_const_val(g, &dest->data.x_array.data.s_none.elements[i], &src->data.x_array.data.s_none.elements[i]); + dest->data.x_array.data.s_none.elements[i].parent.id = ConstParentIdArray; + dest->data.x_array.data.s_none.elements[i].parent.data.p_array.array_val = dest; + dest->data.x_array.data.s_none.elements[i].parent.data.p_array.elem_index = i; + } + break; + } + case ConstArraySpecialUndef: { + // Nothing to copy; the above memcpy did everything we needed. + break; + } + case ConstArraySpecialBuf: { + dest->data.x_array.data.s_buf = buf_create_from_buf(src->data.x_array.data.s_buf); + break; } } } else if (type_has_optional_repr(dest->type) && dest->data.x_optional != nullptr) { From bee4007ec9a353b027654ccb4beeaf3d04dfa26f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 15:49:19 -0500 Subject: [PATCH 66/99] fix crash with multiple comptime fn calls and... ...default initialized array to undefined closes #4578 --- src/analyze.cpp | 39 +++++++++++++++++++++++++++-------- test/stage1/behavior/eval.zig | 25 ++++++++++++++++++++++ 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 9b776da930..98a93d03bb 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5429,6 +5429,8 @@ static bool can_mutate_comptime_var_state(ZigValue *value) { return value->data.x_ptr.mut == ConstPtrMutComptimeVar; case ZigTypeIdArray: + if (value->special == ConstValSpecialUndef) + return false; if (value->type->data.array.len == 0) return false; switch (value->data.x_array.special) { @@ -6701,8 +6703,16 @@ bool const_values_equal_ptr(ZigValue *a, ZigValue *b) { } static bool const_values_equal_array(CodeGen *g, ZigValue *a, ZigValue *b, size_t len) { - assert(a->data.x_array.special != ConstArraySpecialUndef); - assert(b->data.x_array.special != ConstArraySpecialUndef); + if (a->data.x_array.special == ConstArraySpecialUndef && + b->data.x_array.special == ConstArraySpecialUndef) + { + return true; + } + if (a->data.x_array.special == ConstArraySpecialUndef || + b->data.x_array.special == ConstArraySpecialUndef) + { + return false; + } if (a->data.x_array.special == ConstArraySpecialBuf && b->data.x_array.special == ConstArraySpecialBuf) { @@ -9398,13 +9408,24 @@ void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src) { dest->data.x_struct.fields[i]->parent.data.p_struct.field_index = i; } } else if (dest->type->id == ZigTypeIdArray) { - if (dest->data.x_array.special == ConstArraySpecialNone) { - dest->data.x_array.data.s_none.elements = g->pass1_arena->allocate(dest->type->data.array.len); - for (uint64_t i = 0; i < dest->type->data.array.len; i += 1) { - copy_const_val(g, &dest->data.x_array.data.s_none.elements[i], &src->data.x_array.data.s_none.elements[i]); - dest->data.x_array.data.s_none.elements[i].parent.id = ConstParentIdArray; - dest->data.x_array.data.s_none.elements[i].parent.data.p_array.array_val = dest; - dest->data.x_array.data.s_none.elements[i].parent.data.p_array.elem_index = i; + switch (dest->data.x_array.special) { + case ConstArraySpecialNone: { + dest->data.x_array.data.s_none.elements = g->pass1_arena->allocate(dest->type->data.array.len); + for (uint64_t i = 0; i < dest->type->data.array.len; i += 1) { + copy_const_val(g, &dest->data.x_array.data.s_none.elements[i], &src->data.x_array.data.s_none.elements[i]); + dest->data.x_array.data.s_none.elements[i].parent.id = ConstParentIdArray; + dest->data.x_array.data.s_none.elements[i].parent.data.p_array.array_val = dest; + dest->data.x_array.data.s_none.elements[i].parent.data.p_array.elem_index = i; + } + break; + } + case ConstArraySpecialUndef: { + // Nothing to copy; the above memcpy did everything we needed. + break; + } + case ConstArraySpecialBuf: { + dest->data.x_array.data.s_buf = buf_create_from_buf(src->data.x_array.data.s_buf); + break; } } } else if (type_has_optional_repr(dest->type) && dest->data.x_optional != nullptr) { diff --git a/test/stage1/behavior/eval.zig b/test/stage1/behavior/eval.zig index 8e70e97aaa..55ace6198c 100644 --- a/test/stage1/behavior/eval.zig +++ b/test/stage1/behavior/eval.zig @@ -807,3 +807,28 @@ test "return 0 from function that has u0 return type" { } } } + +test "two comptime calls with array default initialized to undefined" { + const S = struct { + const CrossTarget = struct { + dynamic_linker: DynamicLinker = DynamicLinker{}, + + pub fn parse() void { + var result: CrossTarget = .{ }; + result.getCpuArch(); + } + + pub fn getCpuArch(self: CrossTarget) void { } + }; + + const DynamicLinker = struct { + buffer: [255]u8 = undefined, + }; + + }; + + comptime { + S.CrossTarget.parse(); + S.CrossTarget.parse(); + } +} From a5a53a182af3715edd9474fe94cad2223a6c7fb9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 16:06:06 -0500 Subject: [PATCH 67/99] fix typo from other commit d2535c003c6188fcc362028e01ef9f7fb3356727 --- src/analyze.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 98a93d03bb..55e65ae2a1 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1135,7 +1135,7 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent // Self-referencing types via pointers are allowed and have non-zero size ZigType *ty = type_val->data.x_type; while (ty->id == ZigTypeIdPointer && - !ty->data.unionation.resolve_loop_flag_zero_bits) + !ty->data.pointer.resolve_loop_flag_zero_bits) { ty = ty->data.pointer.child_type; } From 8691d3c50d65c3e9e399c83cf2fc1a1173acb6d2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 17:23:16 -0500 Subject: [PATCH 68/99] improve std.zig.system.NativeTargetInfo.detect It now takes a std.zig.CrossTarget parameter, and only resolves native things, leaving explicitly overridden things alone. --- lib/std/zig/cross_target.zig | 18 ++-- lib/std/zig/system.zig | 154 ++++++++++++++++++++++++++--------- src-self-hosted/stage2.zig | 50 ++++-------- 3 files changed, 141 insertions(+), 81 deletions(-) diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 212bc8eb9a..7ce35a26dc 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -7,19 +7,17 @@ const mem = std.mem; /// The purpose of this abstraction is to provide meaningful and unsurprising defaults. /// This struct does reference any resources and it is copyable. pub const CrossTarget = struct { - /// `null` means native. + /// `null` means native. If this is `null` then `cpu_model` must be `null`. cpu_arch: ?Target.Cpu.Arch = null, - /// If `cpu_arch` is native, `null` means native. Otherwise it means baseline. + /// `null` means native. /// If this is non-null, `cpu_arch` must be specified. cpu_model: ?*const Target.Cpu.Model = null, /// Sparse set of CPU features to add to the set from `cpu_model`. - /// If this is non-empty, `cpu_arch` must be specified. cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty, /// Sparse set of CPU features to remove from the set from `cpu_model`. - /// If this is non-empty, `cpu_arch` must be specified. cpu_features_sub: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty, /// `null` means native. @@ -33,13 +31,13 @@ pub const CrossTarget = struct { /// When `os_tag` is native, `null` means equal to the native OS version. os_version_max: ?OsVersion = null, - /// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI. - abi: ?Target.Abi = null, - /// `null` means default when cross compiling, or native when os_tag is native. /// If `isGnuLibC()` is `false`, this must be `null` and is ignored. glibc_version: ?SemVer = null, + /// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI. + abi: ?Target.Abi = null, + /// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path /// based on the `os_tag`. dynamic_linker: DynamicLinker = DynamicLinker{}, @@ -146,6 +144,7 @@ pub const CrossTarget = struct { } } + /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. pub fn toTarget(self: CrossTarget) Target { return .{ .cpu = self.getCpu(), @@ -307,6 +306,7 @@ pub const CrossTarget = struct { return result; } + /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. pub fn getCpu(self: CrossTarget) Target.Cpu { if (self.cpu_arch) |arch| { if (self.cpu_model) |model| { @@ -342,6 +342,7 @@ pub const CrossTarget = struct { return self.getCpu().features; } + /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. pub fn getOs(self: CrossTarget) Target.Os { // `Target.current.os` works when doing `zig build` because Zig generates a build executable using // native OS version range. However this will not be accurate otherwise, and @@ -378,6 +379,7 @@ pub const CrossTarget = struct { return self.os_tag orelse Target.current.os.tag; } + /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. pub fn getOsVersionMin(self: CrossTarget) OsVersion { if (self.os_version_min) |version_min| return version_min; var tmp: CrossTarget = undefined; @@ -385,6 +387,7 @@ pub const CrossTarget = struct { return tmp.os_version_min.?; } + /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. pub fn getOsVersionMax(self: CrossTarget) OsVersion { if (self.os_version_max) |version_max| return version_max; var tmp: CrossTarget = undefined; @@ -392,6 +395,7 @@ pub const CrossTarget = struct { return tmp.os_version_max.?; } + /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. pub fn getAbi(self: CrossTarget) Target.Abi { if (self.abi) |abi| return abi; diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 44ff9af674..f31044b811 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -7,6 +7,7 @@ const ArrayList = std.ArrayList; const assert = std.debug.assert; const process = std.process; const Target = std.Target; +const CrossTarget = std.zig.CrossTarget; const is_windows = Target.current.os.tag == .windows; @@ -182,37 +183,87 @@ pub const NativeTargetInfo = struct { DeviceBusy, }; - /// Detects the native CPU model & features, operating system & version, and C ABI & dynamic linker. - /// On Linux, this is additionally responsible for detecting the native glibc version when applicable. + /// Given a `CrossTarget`, which specifies in detail which parts of the target should be detected + /// natively, which should be standard or default, and which are provided explicitly, this function + /// resolves the native components by detecting the native system, and then resolves standard/default parts + /// relative to that. /// Any resources this function allocates are released before returning, and so there is no /// deinitialization method. /// TODO Remove the Allocator requirement from this function. - pub fn detect(allocator: *Allocator) DetectError!NativeTargetInfo { - const arch = Target.current.cpu.arch; - const os_tag = Target.current.os.tag; - - // TODO Detect native CPU model & features. Until that is implemented we hard code baseline. - const cpu = Target.Cpu.baseline(arch); - - // TODO Detect native operating system version. Until that is implemented we use the default range. - var os = Target.Os.defaultVersionRange(os_tag); - switch (Target.current.os.tag) { - .linux => { - const uts = std.os.uname(); - const release = mem.toSliceConst(u8, @ptrCast([*:0]const u8, &uts.release)); - if (std.builtin.Version.parse(release)) |ver| { - os.version_range.linux.range.min = ver; - os.version_range.linux.range.max = ver; - } else |err| switch (err) { - error.Overflow => {}, - error.InvalidCharacter => {}, - error.InvalidVersion => {}, + pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo { + const cpu = blk: { + if (cross_target.cpu_arch) |arch| { + if (cross_target.cpu_model) |model| { + var adjusted_model = model.toCpu(arch); + cross_target.updateCpuFeatures(&adjusted_model.features); + break :blk adjusted_model; + } else { + // TODO Detect native CPU model. Until that is implemented we use baseline. + var adjusted_baseline = Target.Cpu.baseline(arch); + cross_target.updateCpuFeatures(&adjusted_baseline.features); + break :blk adjusted_baseline; } - }, - else => {}, + } else { + assert(cross_target.cpu_model == null); + // TODO Detect native CPU model & features. Until that is implemented we use baseline. + var adjusted_baseline = Target.Cpu.baseline(Target.current.cpu.arch); + cross_target.updateCpuFeatures(&adjusted_baseline.features); + break :blk adjusted_baseline; + } + }; + + var os = Target.Os.defaultVersionRange(cross_target.getOsTag()); + if (cross_target.os_tag == null) { + switch (Target.current.os.tag) { + .linux => { + const uts = std.os.uname(); + const release = mem.toSliceConst(u8, @ptrCast([*:0]const u8, &uts.release)); + if (std.builtin.Version.parse(release)) |ver| { + os.version_range.linux.range.min = ver; + os.version_range.linux.range.max = ver; + } else |err| switch (err) { + error.Overflow => {}, + error.InvalidCharacter => {}, + error.InvalidVersion => {}, + } + }, + .windows => { + // TODO Detect native operating system version. + }, + .macosx => { + // TODO Detect native operating system version. + }, + .freebsd => { + // TODO Detect native operating system version. + }, + else => {}, + } } - return detectAbiAndDynamicLinker(allocator, cpu, os); + if (cross_target.os_version_min) |min| switch (min) { + .none => {}, + .semver => |semver| switch (cross_target.getOsTag()) { + .linux => os.version_range.linux.range.min = semver, + else => os.version_range.semver.min = semver, + }, + .windows => |win_ver| os.version_range.windows.min = win_ver, + }; + + if (cross_target.os_version_max) |max| switch (max) { + .none => {}, + .semver => |semver| switch (cross_target.getOsTag()) { + .linux => os.version_range.linux.range.max = semver, + else => os.version_range.semver.max = semver, + }, + .windows => |win_ver| os.version_range.windows.max = win_ver, + }; + + if (cross_target.glibc_version) |glibc| { + assert(cross_target.isGnuLibC()); + os.version_range.linux.glibc = glibc; + } + + return detectAbiAndDynamicLinker(allocator, cpu, os, cross_target); } /// First we attempt to use the executable's own binary. If it is dynamically @@ -224,9 +275,14 @@ pub const NativeTargetInfo = struct { allocator: *Allocator, cpu: Target.Cpu, os: Target.Os, + cross_target: CrossTarget, ) DetectError!NativeTargetInfo { - if (!comptime Target.current.hasDynamicLinker()) { - return defaultAbiAndDynamicLinker(cpu, os); + const native_target_has_ld = comptime Target.current.hasDynamicLinker(); + const is_linux = Target.current.os.tag == .linux; + const have_all_info = cross_target.dynamic_linker.get() != null and + cross_target.abi != null and (!is_linux or cross_target.abi.?.isGnu()); + if (!native_target_has_ld or have_all_info) { + return defaultAbiAndDynamicLinker(cpu, os, cross_target); } // The current target's ABI cannot be relied on for this. For example, we may build the zig // compiler for target riscv64-linux-musl and provide a tarball for users to download. @@ -264,6 +320,13 @@ pub const NativeTargetInfo = struct { } const ld_info_list = ld_info_list_buffer[0..ld_info_list_len]; + if (cross_target.dynamic_linker.get()) |explicit_ld| { + const explicit_ld_basename = fs.path.basename(explicit_ld); + for (ld_info_list) |ld_info| { + const standard_ld_basename = fs.path.basename(ld_info.ld.get().?); + } + } + // Best case scenario: the executable is dynamically linked, and we can iterate // over our own shared objects and find a dynamic linker. self_exe: { @@ -288,7 +351,9 @@ pub const NativeTargetInfo = struct { // Look for glibc version. var os_adjusted = os; - if (Target.current.os.tag == .linux and found_ld_info.abi.isGnu()) { + if (Target.current.os.tag == .linux and found_ld_info.abi.isGnu() and + cross_target.glibc_version == null) + { for (lib_paths) |lib_path| { if (std.mem.endsWith(u8, lib_path, glibc_so_basename)) { os_adjusted.version_range.linux.glibc = glibcVerFromSO(lib_path) catch |err| switch (err) { @@ -306,9 +371,12 @@ pub const NativeTargetInfo = struct { .target = .{ .cpu = cpu, .os = os_adjusted, - .abi = found_ld_info.abi, + .abi = cross_target.abi orelse found_ld_info.abi, }, - .dynamic_linker = DynamicLinker.init(found_ld_path), + .dynamic_linker = if (cross_target.dynamic_linker.get() == null) + DynamicLinker.init(found_ld_path) + else + cross_target.dynamic_linker, }; return result; } @@ -317,7 +385,7 @@ pub const NativeTargetInfo = struct { // trick won't work. The next thing we fall back to is the same thing, but for /usr/bin/env. // Since that path is hard-coded into the shebang line of many portable scripts, it's a // reasonably reliable path to check for. - return abiAndDynamicLinkerFromUsrBinEnv(cpu, os, ld_info_list) catch |err| switch (err) { + return abiAndDynamicLinkerFromUsrBinEnv(cpu, os, ld_info_list, cross_target) catch |err| switch (err) { error.FileSystem, error.SystemResources, error.SymLinkLoop, @@ -337,7 +405,7 @@ pub const NativeTargetInfo = struct { error.UnexpectedEndOfFile, error.NameTooLong, // Finally, we fall back on the standard path. - => defaultAbiAndDynamicLinker(cpu, os), + => defaultAbiAndDynamicLinker(cpu, os, cross_target), }; } @@ -379,6 +447,7 @@ pub const NativeTargetInfo = struct { cpu: Target.Cpu, os: Target.Os, ld_info_list: []const LdInfo, + cross_target: CrossTarget, ) !NativeTargetInfo { const env_file = std.fs.openFileAbsoluteC("/usr/bin/env", .{}) catch |err| switch (err) { error.NoSpaceLeft => unreachable, @@ -424,10 +493,12 @@ pub const NativeTargetInfo = struct { .target = .{ .cpu = cpu, .os = os, - .abi = Target.Abi.default(cpu.arch, os), + .abi = cross_target.abi orelse Target.Abi.default(cpu.arch, os), }, + .dynamic_linker = cross_target.dynamic_linker, }; var rpath_offset: ?u64 = null; // Found inside PT_DYNAMIC + const look_for_ld = cross_target.dynamic_linker.get() == null; var ph_buf: [16 * @sizeOf(elf.Elf64_Phdr)]u8 align(@alignOf(elf.Elf64_Phdr)) = undefined; if (phentsize > @sizeOf(elf.Elf64_Phdr)) return error.InvalidElfFile; @@ -448,7 +519,7 @@ pub const NativeTargetInfo = struct { const ph64 = @ptrCast(*elf.Elf64_Phdr, @alignCast(@alignOf(elf.Elf64_Phdr), &ph_buf[ph_buf_i])); const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type); switch (p_type) { - elf.PT_INTERP => { + elf.PT_INTERP => if (look_for_ld) { const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong; @@ -470,7 +541,9 @@ pub const NativeTargetInfo = struct { } }, // We only need this for detecting glibc version. - elf.PT_DYNAMIC => if (Target.current.os.tag == .linux and result.target.isGnuLibC()) { + elf.PT_DYNAMIC => if (Target.current.os.tag == .linux and result.target.isGnuLibC() and + cross_target.glibc_version == null) + { var dyn_off = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); const dyn_size: u64 = if (is_64) @sizeOf(elf.Elf64_Dyn) else @sizeOf(elf.Elf32_Dyn); @@ -515,7 +588,7 @@ pub const NativeTargetInfo = struct { } } - if (Target.current.os.tag == .linux and result.target.isGnuLibC()) { + if (Target.current.os.tag == .linux and result.target.isGnuLibC() and cross_target.glibc_version == null) { if (rpath_offset) |rpoff| { const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); @@ -657,15 +730,18 @@ pub const NativeTargetInfo = struct { return i; } - fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { + fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, cross_target: CrossTarget) !NativeTargetInfo { const target: Target = .{ .cpu = cpu, .os = os, - .abi = Target.Abi.default(cpu.arch, os), + .abi = cross_target.abi orelse Target.Abi.default(cpu.arch, os), }; return NativeTargetInfo{ .target = target, - .dynamic_linker = target.standardDynamicLinkerPath(), + .dynamic_linker = if (cross_target.dynamic_linker.get() == null) + target.standardDynamicLinkerPath() + else + cross_target.dynamic_linker, }; } diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 726c65937e..bef13a08e9 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1152,44 +1152,24 @@ fn enumInt(comptime Enum: type, int: c_int) Enum { return @intToEnum(Enum, @intCast(@TagType(Enum), int)); } -/// TODO move dynamic linker to be part of the target -/// TODO self-host this function fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target { - var adjusted_target = cross_target.toTarget(); - var have_native_dl = false; - if (cross_target.cpu_arch == null or cross_target.os_tag == null) { - const detected_info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator); - if (cross_target.cpu_arch == null) { - adjusted_target.cpu = detected_info.target.cpu; - - // TODO We want to just use detected_info.target but implementing - // CPU model & feature detection is todo so here we rely on LLVM. - // There is another occurrence of this; search for detectNativeCpuWithLLVM. - const llvm = @import("llvm.zig"); - const llvm_cpu_name = llvm.GetHostCPUName(); - const llvm_cpu_features = llvm.GetNativeFeatures(); - const arch = std.Target.current.cpu.arch; - adjusted_target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); - } - if (cross_target.os_tag == null) { - adjusted_target.os = detected_info.target.os; - - if (detected_info.dynamic_linker.get()) |dl| { - have_native_dl = true; - dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl); - } - if (cross_target.abi == null) { - adjusted_target.abi = detected_info.target.abi; - } - } else if (cross_target.abi == null) { - adjusted_target.abi = Target.Abi.default(adjusted_target.cpu.arch, adjusted_target.os); - } + var info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target); + if (cross_target.cpu_arch == null or cross_target.cpu_model == null) { + // TODO We want to just use detected_info.target but implementing + // CPU model & feature detection is todo so here we rely on LLVM. + const llvm = @import("llvm.zig"); + const llvm_cpu_name = llvm.GetHostCPUName(); + const llvm_cpu_features = llvm.GetNativeFeatures(); + const arch = std.Target.current.cpu.arch; + info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); + cross_target.updateCpuFeatures(&info.target.cpu.features); } - if (!have_native_dl) { - const dl = adjusted_target.standardDynamicLinkerPath(); - dynamic_linker_ptr.* = if (dl.get()) |s| try mem.dupeZ(std.heap.c_allocator, u8, s) else null; + if (info.dynamic_linker.get()) |dl| { + dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl); + } else { + dynamic_linker_ptr.* = null; } - return adjusted_target; + return info.target; } // ABI warning From aa13f339d4d91f8e39f005821c172290e1a0227f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 18:09:33 -0500 Subject: [PATCH 69/99] fix handling of CrossTarget.cpu_model --- lib/std/zig/cross_target.zig | 16 ++++++++++++++-- lib/std/zig/system.zig | 19 ++++++------------- src-self-hosted/stage2.zig | 1 + 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 7ce35a26dc..a2d265707b 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -10,8 +10,7 @@ pub const CrossTarget = struct { /// `null` means native. If this is `null` then `cpu_model` must be `null`. cpu_arch: ?Target.Cpu.Arch = null, - /// `null` means native. - /// If this is non-null, `cpu_arch` must be specified. + /// `null` means native. If this is non-null, `cpu_arch` must be specified. cpu_model: ?*const Target.Cpu.Model = null, /// Sparse set of CPU features to add to the set from `cpu_model`. @@ -301,6 +300,10 @@ pub const CrossTarget = struct { return error.UnknownCpuFeature; } } + } else if (arch_is_native) { + result.cpu_model = null; + } else { + result.cpu_model = Target.Cpu.Model.baseline(arch); } return result; @@ -725,6 +728,15 @@ pub const CrossTarget = struct { }; test "CrossTarget.parse" { + { + const cross_target = try CrossTarget.parse(.{ + .arch_os_abi = "aarch64-linux", + .cpu_features = "native", + }); + + std.testing.expect(cross_target.cpu_arch.? == .aarch64); + std.testing.expect(cross_target.cpu_model == null); + } { const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "native" }); diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index f31044b811..b712c7c062 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -192,21 +192,14 @@ pub const NativeTargetInfo = struct { /// TODO Remove the Allocator requirement from this function. pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo { const cpu = blk: { - if (cross_target.cpu_arch) |arch| { - if (cross_target.cpu_model) |model| { - var adjusted_model = model.toCpu(arch); - cross_target.updateCpuFeatures(&adjusted_model.features); - break :blk adjusted_model; - } else { - // TODO Detect native CPU model. Until that is implemented we use baseline. - var adjusted_baseline = Target.Cpu.baseline(arch); - cross_target.updateCpuFeatures(&adjusted_baseline.features); - break :blk adjusted_baseline; - } + const arch = cross_target.getCpuArch(); + if (cross_target.cpu_model) |model| { + var adjusted_model = model.toCpu(arch); + cross_target.updateCpuFeatures(&adjusted_model.features); + break :blk adjusted_model; } else { - assert(cross_target.cpu_model == null); // TODO Detect native CPU model & features. Until that is implemented we use baseline. - var adjusted_baseline = Target.Cpu.baseline(Target.current.cpu.arch); + var adjusted_baseline = Target.Cpu.baseline(arch); cross_target.updateCpuFeatures(&adjusted_baseline.features); break :blk adjusted_baseline; } diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index bef13a08e9..696d7ea760 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1163,6 +1163,7 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) const arch = std.Target.current.cpu.arch; info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); cross_target.updateCpuFeatures(&info.target.cpu.features); + info.target.cpu.arch = cross_target.getCpuArch(); } if (info.dynamic_linker.get()) |dl| { dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl); From 4591236ae1de25e55a7d134d5cc0dcc3e1ad6a6e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 18:31:46 -0500 Subject: [PATCH 70/99] CrossTarget.cpu_model: communicate intent precisely --- lib/std/zig/cross_target.zig | 75 ++++++++++++++++++++++-------------- lib/std/zig/system.zig | 31 ++++++++++----- src-self-hosted/stage2.zig | 2 +- 3 files changed, 69 insertions(+), 39 deletions(-) diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index a2d265707b..16c0a64f6b 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -7,11 +7,10 @@ const mem = std.mem; /// The purpose of this abstraction is to provide meaningful and unsurprising defaults. /// This struct does reference any resources and it is copyable. pub const CrossTarget = struct { - /// `null` means native. If this is `null` then `cpu_model` must be `null`. + /// `null` means native. cpu_arch: ?Target.Cpu.Arch = null, - /// `null` means native. If this is non-null, `cpu_arch` must be specified. - cpu_model: ?*const Target.Cpu.Model = null, + cpu_model: CpuModel = CpuModel.determined_by_cpu_arch, /// Sparse set of CPU features to add to the set from `cpu_model`. cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty, @@ -41,6 +40,20 @@ pub const CrossTarget = struct { /// based on the `os_tag`. dynamic_linker: DynamicLinker = DynamicLinker{}, + pub const CpuModel = union(enum) { + /// Always native + native, + + /// Always baseline + baseline, + + /// If CPU Architecture is native, then the CPU model will be native. Otherwise, + /// it will be baseline. + determined_by_cpu_arch, + + explicit: *const Target.Cpu.Model, + }; + pub const OsVersion = union(enum) { none: void, semver: SemVer, @@ -54,7 +67,7 @@ pub const CrossTarget = struct { pub fn fromTarget(target: Target) CrossTarget { var result: CrossTarget = .{ .cpu_arch = target.cpu.arch, - .cpu_model = target.cpu.model, + .cpu_model = .{ .explicit = target.cpu.model }, .os_tag = target.os.tag, .os_version_min = undefined, .os_version_max = undefined, @@ -266,11 +279,11 @@ pub const CrossTarget = struct { const add_set = &result.cpu_features_add; const sub_set = &result.cpu_features_sub; if (mem.eql(u8, cpu_name, "native")) { - result.cpu_model = null; + result.cpu_model = .native; } else if (mem.eql(u8, cpu_name, "baseline")) { - result.cpu_model = Target.Cpu.Model.baseline(arch); + result.cpu_model = .baseline; } else { - result.cpu_model = try arch.parseCpuModel(cpu_name); + result.cpu_model = .{ .explicit = try arch.parseCpuModel(cpu_name) }; } while (index < cpu_features.len) { @@ -300,10 +313,6 @@ pub const CrossTarget = struct { return error.UnknownCpuFeature; } } - } else if (arch_is_native) { - result.cpu_model = null; - } else { - result.cpu_model = Target.Cpu.Model.baseline(arch); } return result; @@ -311,24 +320,33 @@ pub const CrossTarget = struct { /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. pub fn getCpu(self: CrossTarget) Target.Cpu { - if (self.cpu_arch) |arch| { - if (self.cpu_model) |model| { - var adjusted_model = model.toCpu(arch); - self.updateCpuFeatures(&adjusted_model.features); - return adjusted_model; - } else { - var adjusted_baseline = Target.Cpu.baseline(arch); + switch (self.cpu_model) { + .native => { + // This works when doing `zig build` because Zig generates a build executable using + // native CPU model & features. However this will not be accurate otherwise, and + // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. + return Target.current.cpu; + }, + .baseline => { + var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch()); self.updateCpuFeatures(&adjusted_baseline.features); return adjusted_baseline; - } - } else { - assert(self.cpu_model == null); - assert(self.cpu_features_sub.isEmpty()); - assert(self.cpu_features_add.isEmpty()); - // This works when doing `zig build` because Zig generates a build executable using - // native CPU model & features. However this will not be accurate otherwise, and - // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. - return Target.current.cpu; + }, + .determined_by_cpu_arch => if (self.cpu_arch == null) { + // This works when doing `zig build` because Zig generates a build executable using + // native CPU model & features. However this will not be accurate otherwise, and + // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. + return Target.current.cpu; + } else { + var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch()); + self.updateCpuFeatures(&adjusted_baseline.features); + return adjusted_baseline; + }, + .explicit => |model| { + var adjusted_model = model.toCpu(self.getCpuArch()); + self.updateCpuFeatures(&adjusted_model.features); + return adjusted_model; + }, } } @@ -461,7 +479,8 @@ pub const CrossTarget = struct { } pub fn isNative(self: CrossTarget) bool { - return self.cpu_arch == null and self.cpu_model == null and + return self.cpu_arch == null and + (self.cpu_model == .native or self.cpu_model == .determined_by_cpu_arch) and self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty() and self.os_tag == null and self.os_version_min == null and self.os_version_max == null and self.abi == null and self.dynamic_linker.get() == null; diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index b712c7c062..5ff17bae23 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -191,18 +191,18 @@ pub const NativeTargetInfo = struct { /// deinitialization method. /// TODO Remove the Allocator requirement from this function. pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo { - const cpu = blk: { - const arch = cross_target.getCpuArch(); - if (cross_target.cpu_model) |model| { - var adjusted_model = model.toCpu(arch); + const cpu = switch (cross_target.cpu_model) { + .native => detectNativeCpuAndFeatures(cross_target), + .baseline => baselineCpuAndFeatures(cross_target), + .determined_by_cpu_arch => if (cross_target.cpu_arch == null) + detectNativeCpuAndFeatures(cross_target) + else + baselineCpuAndFeatures(cross_target), + .explicit => |model| blk: { + var adjusted_model = model.toCpu(cross_target.getCpuArch()); cross_target.updateCpuFeatures(&adjusted_model.features); break :blk adjusted_model; - } else { - // TODO Detect native CPU model & features. Until that is implemented we use baseline. - var adjusted_baseline = Target.Cpu.baseline(arch); - cross_target.updateCpuFeatures(&adjusted_baseline.features); - break :blk adjusted_baseline; - } + }, }; var os = Target.Os.defaultVersionRange(cross_target.getOsTag()); @@ -758,4 +758,15 @@ pub const NativeTargetInfo = struct { } } } + + fn detectNativeCpuAndFeatures(cross_target: CrossTarget) Target.Cpu { + // TODO Detect native CPU model & features. Until that is implemented we use baseline. + return baselineCpuAndFeatures(cross_target); + } + + fn baselineCpuAndFeatures(cross_target: CrossTarget) Target.Cpu { + var adjusted_baseline = Target.Cpu.baseline(cross_target.getCpuArch()); + cross_target.updateCpuFeatures(&adjusted_baseline.features); + return adjusted_baseline; + } }; diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 696d7ea760..a8aa10d91e 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1154,7 +1154,7 @@ fn enumInt(comptime Enum: type, int: c_int) Enum { fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target { var info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target); - if (cross_target.cpu_arch == null or cross_target.cpu_model == null) { + if (cross_target.cpu_arch == null or cross_target.cpu_model == .native) { // TODO We want to just use detected_info.target but implementing // CPU model & feature detection is todo so here we rely on LLVM. const llvm = @import("llvm.zig"); From 7e3bb00a0e919bdb2a778a7efb648dfc6afa8318 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 18:48:19 -0500 Subject: [PATCH 71/99] don't choose native ld path when os is non native --- lib/std/zig/system.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 5ff17bae23..aa8def32a9 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -274,7 +274,8 @@ pub const NativeTargetInfo = struct { const is_linux = Target.current.os.tag == .linux; const have_all_info = cross_target.dynamic_linker.get() != null and cross_target.abi != null and (!is_linux or cross_target.abi.?.isGnu()); - if (!native_target_has_ld or have_all_info) { + const os_is_non_native = cross_target.os_tag != null; + if (!native_target_has_ld or have_all_info or os_is_non_native) { return defaultAbiAndDynamicLinker(cpu, os, cross_target); } // The current target's ABI cannot be relied on for this. For example, we may build the zig From 4bc893c346aa45972ae884188388269b66efc3b9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 19:05:14 -0500 Subject: [PATCH 72/99] update test to newer API --- lib/std/zig/cross_target.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 16c0a64f6b..66b7344840 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -754,7 +754,7 @@ test "CrossTarget.parse" { }); std.testing.expect(cross_target.cpu_arch.? == .aarch64); - std.testing.expect(cross_target.cpu_model == null); + std.testing.expect(cross_target.cpu_model == .native); } { const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "native" }); From e683eee415bc4362f9c0e095ceecebe577c47d06 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 19:18:27 -0500 Subject: [PATCH 73/99] fix CrossTarget.isNative, setGnuLibCVersion, zigTriple --- lib/std/zig/cross_target.zig | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 66b7344840..08a8d21fad 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -483,7 +483,7 @@ pub const CrossTarget = struct { (self.cpu_model == .native or self.cpu_model == .determined_by_cpu_arch) and self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty() and self.os_tag == null and self.os_version_min == null and self.os_version_max == null and - self.abi == null and self.dynamic_linker.get() == null; + self.abi == null and self.dynamic_linker.get() == null and self.glibc_version == null; } pub fn zigTriple(self: CrossTarget, allocator: *mem.Allocator) error{OutOfMemory}![:0]u8 { @@ -514,13 +514,10 @@ pub const CrossTarget = struct { } } - if (self.abi) |abi| { + if (self.glibc_version) |v| { + try result.print("-{}.{}", .{ @tagName(self.getAbi()), v }); + } else if (self.abi) |abi| { try result.print("-{}", .{@tagName(abi)}); - if (self.glibc_version) |v| { - try result.print(".{}", .{v}); - } - } else { - assert(self.glibc_version == null); } return result.toOwnedSlice(); @@ -643,7 +640,7 @@ pub const CrossTarget = struct { return Target.isGnuLibC_os_tag_abi(self.getOsTag(), self.getAbi()); } - pub fn setGnuLibCVersion(self: CrossTarget, major: u32, minor: u32, patch: u32) void { + pub fn setGnuLibCVersion(self: *CrossTarget, major: u32, minor: u32, patch: u32) void { assert(self.isGnuLibC()); self.glibc_version = SemVer{ .major = major, .minor = minor, .patch = patch }; } @@ -747,6 +744,14 @@ pub const CrossTarget = struct { }; test "CrossTarget.parse" { + if (Target.current.isGnuLibC()) { + var cross_target = try CrossTarget.parse(.{}); + cross_target.setGnuLibCVersion(2, 1, 1); + + const text = try cross_target.zigTriple(std.testing.allocator); + defer std.testing.allocator.free(text); + std.testing.expectEqualSlices(u8, "native-native-gnu.2.1.1", text); + } { const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "aarch64-linux", From 1aef0bef754ddf1943e22740ef045fab7769cde1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Feb 2020 19:24:52 -0500 Subject: [PATCH 74/99] std.Target.Os.WindowsVersion: non-exhaustive enum --- lib/std/target.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/target.zig b/lib/std/target.zig index 3054c60467..5807ba65ef 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -91,6 +91,7 @@ pub const Target = struct { win10_rs4 = 0x0A000005, win10_rs5 = 0x0A000006, win10_19h1 = 0x0A000007, + _, pub const Range = struct { min: WindowsVersion, From 3cba603eae1a1c8b0338f5584041c73d55682c0a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 29 Feb 2020 01:05:11 -0500 Subject: [PATCH 75/99] fix crash when building docgen --- src/analyze.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 90037a1191..1a9172c030 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -6734,8 +6734,6 @@ static bool const_values_equal_array(CodeGen *g, ZigValue *a, ZigValue *b, size_ bool const_values_equal(CodeGen *g, ZigValue *a, ZigValue *b) { if (a->type->id != b->type->id) return false; - assert(a->special == ConstValSpecialStatic); - assert(b->special == ConstValSpecialStatic); if (a->type == b->type) { switch (type_has_one_possible_value(g, a->type)) { case OnePossibleValueInvalid: @@ -6746,6 +6744,11 @@ bool const_values_equal(CodeGen *g, ZigValue *a, ZigValue *b) { return true; } } + if (a->special == ConstValSpecialUndef || b->special == ConstValSpecialUndef) { + return a->special == b->special; + } + assert(a->special == ConstValSpecialStatic); + assert(b->special == ConstValSpecialStatic); switch (a->type->id) { case ZigTypeIdOpaque: zig_unreachable(); From 1b41f2d77ea14de27b0655a35a727d664cfc4ca4 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Sat, 29 Feb 2020 08:36:42 +0200 Subject: [PATCH 76/99] C pointer slices are no longer allowzero (#4462) * Slices from C pointers are no longer allowzero but instead insert a runtime assertion. * Added a test, fixed code for cases with non-allowzero C pointers * Create new type when flipping allow_zero, sometimes we get a cached value back from adjust_ptr_len. * Added comments, changed panic message * Added runtime safety test. --- src/codegen.cpp | 2 +- src/ir.cpp | 35 +++++++++++++++++++++++++++++++--- test/runtime_safety.zig | 13 +++++++++++++ test/stage1/behavior/slice.zig | 11 +++++++++++ 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index ee3e05a801..69220382cd 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -973,7 +973,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { case PanicMsgIdExactDivisionRemainder: return buf_create_from_str("exact division produced remainder"); case PanicMsgIdUnwrapOptionalFail: - return buf_create_from_str("attempt to unwrap null"); + return buf_create_from_str("attempt to use null value"); case PanicMsgIdUnreachable: return buf_create_from_str("reached unreachable code"); case PanicMsgIdInvalidErrorCode: diff --git a/src/ir.cpp b/src/ir.cpp index 895cfa334c..72e247b75a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -20412,6 +20412,17 @@ static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) { ptr_type->data.pointer.allow_zero); } +static ZigType *adjust_ptr_allow_zero(CodeGen *g, ZigType *ptr_type, bool allow_zero) { + assert(ptr_type->id == ZigTypeIdPointer); + return get_pointer_to_type_extra(g, + ptr_type->data.pointer.child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.ptr_len, + ptr_type->data.pointer.explicit_alignment, + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, + allow_zero); +} + static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemPtr *elem_ptr_instruction) { Error err; IrInstGen *array_ptr = elem_ptr_instruction->array_ptr->child; @@ -25966,6 +25977,8 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i ZigType *non_sentinel_slice_ptr_type; ZigType *elem_type; + bool generate_non_null_assert = false; + if (array_type->id == ZigTypeIdArray) { elem_type = array_type->data.array.child_type; bool is_comptime_const = ptr_ptr->value->special == ConstValSpecialStatic && @@ -25993,6 +26006,14 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i elem_type = array_type->data.pointer.child_type; if (array_type->data.pointer.ptr_len == PtrLenC) { array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown); + + // C pointers are allowzero by default. + // However, we want to be able to slice them without generating an allowzero slice (see issue #4401). + // To achieve this, we generate a runtime safety check and make the slice type non-allowzero. + if (array_type->data.pointer.allow_zero) { + array_type = adjust_ptr_allow_zero(ira->codegen, array_type, false); + generate_non_null_assert = true; + } } ZigType *maybe_sentineled_slice_ptr_type = array_type; non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); @@ -26264,7 +26285,6 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, return_type, nullptr, true, true); - if (result_loc != nullptr) { if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { return result_loc; @@ -26277,8 +26297,17 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i return ira->codegen->invalid_inst_gen; } - return ir_build_slice_gen(ira, &instruction->base.base, return_type, - ptr_ptr, casted_start, end, instruction->safety_check_on, result_loc); + if (generate_non_null_assert) { + IrInstGen *ptr_val = ir_get_deref(ira, &instruction->base.base, ptr_ptr, nullptr); + + if (type_is_invalid(ptr_val->value->type)) + return ira->codegen->invalid_inst_gen; + + ir_build_assert_non_null(ira, &instruction->base.base, ptr_val); + } + + return ir_build_slice_gen(ira, &instruction->base.base, return_type, ptr_ptr, + casted_start, end, instruction->safety_check_on, result_loc); } static IrInstGen *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstSrcHasField *instruction) { diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index e4ea7f52db..e183c6979f 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -745,4 +745,17 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ (await p) catch unreachable; \\} ); + + // Slicing a C pointer returns a non-allowzero slice, thus we need to emit + // a safety check to ensure the pointer is not null. + cases.addRuntimeSafety("slicing null C pointer", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\ + \\pub fn main() void { + \\ var ptr: [*c]const u32 = null; + \\ var slice = ptr[0..3]; + \\} + ); } diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig index c0fa723cfb..9dd57f474e 100644 --- a/test/stage1/behavior/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -43,6 +43,17 @@ test "C pointer" { expectEqualSlices(u8, "kjdhfkjdhf", slice); } +test "C pointer slice access" { + var buf: [10]u32 = [1]u32{42} ** 10; + const c_ptr = @ptrCast([*c]const u32, &buf); + + comptime expectEqual([]const u32, @TypeOf(c_ptr[0..1])); + + for (c_ptr[0..5]) |*cl| { + expectEqual(@as(u32, 42), cl.*); + } +} + fn sliceSum(comptime q: []const u8) i32 { comptime var result = 0; inline for (q) |item| { From 7e6b68a534f840d2d08770a5a77896707271e8ea Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 29 Feb 2020 02:08:26 -0500 Subject: [PATCH 77/99] std.Target.standardDynamicLinkerPath: delete bad doc comment --- lib/std/target.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index 5807ba65ef..0082510505 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1133,8 +1133,6 @@ pub const Target = struct { } }; - /// The result will be a byte index *pointing at the final byte*. In other words, length minus one. - /// A return value of `null` means the concept of a dynamic linker is not meaningful for that target. pub fn standardDynamicLinkerPath(self: Target) DynamicLinker { var result: DynamicLinker = .{}; const S = struct { From f072b0c056768648eb56230036dc8c973116d066 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 29 Feb 2020 12:00:58 +0100 Subject: [PATCH 78/99] target: Implement OS version detection for Windows Closes #4581 --- lib/std/os/windows/bits.zig | 13 ++++++++++++ lib/std/os/windows/ntdll.zig | 10 ++++++++- lib/std/zig/system.zig | 40 +++++++++++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/lib/std/os/windows/bits.zig b/lib/std/os/windows/bits.zig index 4e153d40ed..761b508373 100644 --- a/lib/std/os/windows/bits.zig +++ b/lib/std/os/windows/bits.zig @@ -1321,3 +1321,16 @@ pub const PSAPI_WS_WATCH_INFORMATION_EX = extern struct { Flags: ULONG_PTR, }; pub const PPSAPI_WS_WATCH_INFORMATION_EX = *PSAPI_WS_WATCH_INFORMATION_EX; + +pub const OSVERSIONINFOW = extern struct { + dwOSVersionInfoSize: ULONG, + dwMajorVersion: ULONG, + dwMinorVersion: ULONG, + dwBuildNumber: ULONG, + dwPlatformId: ULONG, + szCSDVersion: [128]WCHAR, +}; +pub const POSVERSIONINFOW = *OSVERSIONINFOW; +pub const LPOSVERSIONINFOW = *OSVERSIONINFOW; +pub const RTL_OSVERSIONINFOW = OSVERSIONINFOW; +pub const PRTL_OSVERSIONINFOW = *RTL_OSVERSIONINFOW; diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig index e98a2e6a87..49e60803bc 100644 --- a/lib/std/os/windows/ntdll.zig +++ b/lib/std/os/windows/ntdll.zig @@ -1,6 +1,14 @@ usingnamespace @import("bits.zig"); -pub extern "NtDll" fn RtlCaptureStackBackTrace(FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **c_void, BackTraceHash: ?*DWORD) callconv(.Stdcall) WORD; +pub extern "NtDll" fn RtlGetVersion( + lpVersionInformation: PRTL_OSVERSIONINFOW, +) callconv(.Stdcall) NTSTATUS; +pub extern "NtDll" fn RtlCaptureStackBackTrace( + FramesToSkip: DWORD, + FramesToCapture: DWORD, + BackTrace: **c_void, + BackTraceHash: ?*DWORD, +) callconv(.Stdcall) WORD; pub extern "NtDll" fn NtQueryInformationFile( FileHandle: HANDLE, IoStatusBlock: *IO_STATUS_BLOCK, diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index aa8def32a9..333a5c3950 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -181,6 +181,7 @@ pub const NativeTargetInfo = struct { ProcessFdQuotaExceeded, SystemFdQuotaExceeded, DeviceBusy, + Unexpected, }; /// Given a `CrossTarget`, which specifies in detail which parts of the target should be detected @@ -221,7 +222,44 @@ pub const NativeTargetInfo = struct { } }, .windows => { - // TODO Detect native operating system version. + var version_info: std.os.windows.RTL_OSVERSIONINFOW = undefined; + version_info.dwOSVersionInfoSize = @sizeOf(@TypeOf(version_info)); + + const rc = std.os.windows.ntdll.RtlGetVersion(&version_info); + switch (rc) { + .SUCCESS => {}, + else => return std.os.windows.unexpectedStatus(rc), + } + + // Starting from the system infos build a NTDDI-like version + // constant whose format is: + // B0 B1 B2 B3 + // `---` `` ``--> Sub-version (Starting from Windows 10 onwards) + // \ `--> Service pack (Always zero in the constants defined) + // `--> OS version (Major & minor) + const os_ver: u16 = // + @intCast(u16, version_info.dwMajorVersion & 0xff) << 8 | + @intCast(u16, version_info.dwMinorVersion & 0xff); + const sp_ver: u8 = 0; + const sub_ver: u8 = if (os_ver >= 0xA000) subver: { + // There's no other way to obtain this info beside + // checking the build number against a known set of + // values + for ([_]u32{ + 10240, 10586, 14393, 15063, 16299, 17134, 17763, + 18362, 18363, + }) |build, i| { + if (version_info.dwBuildNumber < build) + break :subver @truncate(u8, i); + } + // Unknown subversion, the OS is too new... + break :subver 0; + } else 0; + + const version: u32 = @as(u32, os_ver) << 16 | @as(u32, sp_ver) << 8 | sub_ver; + + os.version_range.windows.max = @intToEnum(Target.Os.WindowsVersion, version); + os.version_range.windows.min = @intToEnum(Target.Os.WindowsVersion, version); }, .macosx => { // TODO Detect native operating system version. From 513076ee9ced78999eae283bde23c64d26330cf7 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Sat, 29 Feb 2020 23:56:17 +1100 Subject: [PATCH 79/99] Fill out PEB definition --- lib/std/os/windows/bits.zig | 174 +++++++++++++++++++++++++++++++++--- 1 file changed, 160 insertions(+), 14 deletions(-) diff --git a/lib/std/os/windows/bits.zig b/lib/std/os/windows/bits.zig index 4e153d40ed..89e02fc7f1 100644 --- a/lib/std/os/windows/bits.zig +++ b/lib/std/os/windows/bits.zig @@ -23,6 +23,7 @@ pub const BOOL = c_int; pub const BOOLEAN = BYTE; pub const BYTE = u8; pub const CHAR = u8; +pub const UCHAR = u8; pub const FLOAT = f32; pub const HANDLE = *c_void; pub const HCRYPTPROV = ULONG_PTR; @@ -54,6 +55,7 @@ pub const WORD = u16; pub const DWORD = u32; pub const DWORD64 = u64; pub const LARGE_INTEGER = i64; +pub const ULARGE_INTEGER = u64; pub const USHORT = u16; pub const SHORT = i16; pub const ULONG = u32; @@ -1145,28 +1147,172 @@ pub const UNICODE_STRING = extern struct { Buffer: [*]WCHAR, }; +const ACTIVATION_CONTEXT_DATA = @OpaqueType(); +const ASSEMBLY_STORAGE_MAP = @OpaqueType(); +const FLS_CALLBACK_INFO = @OpaqueType(); +const RTL_BITMAP = @OpaqueType(); +pub const PRTL_BITMAP = *RTL_BITMAP; +const KAFFINITY = usize; + +/// Process Environment Block +/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including: +/// - https://github.com/wine-mirror/wine/blob/1aff1e6a370ee8c0213a0fd4b220d121da8527aa/include/winternl.h#L269 +/// - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/index.htm pub const PEB = extern struct { - Reserved1: [2]BYTE, - BeingDebugged: BYTE, - Reserved2: [1]BYTE, - Reserved3: [2]PVOID, + // Versions: All + InheritedAddressSpace: BOOLEAN, + + // Versions: 3.51+ + ReadImageFileExecOptions: BOOLEAN, + BeingDebugged: BOOLEAN, + + // Versions: 5.2+ (previously was padding) + BitField: UCHAR, + + // Versions: all + Mutant: HANDLE, + ImageBaseAddress: HMODULE, Ldr: *PEB_LDR_DATA, ProcessParameters: *RTL_USER_PROCESS_PARAMETERS, - Reserved4: [3]PVOID, + SubSystemData: PVOID, + ProcessHeap: HANDLE, + + // Versions: 5.1+ + FastPebLock: *RTL_CRITICAL_SECTION, + + // Versions: 5.2+ AtlThunkSListPtr: PVOID, - Reserved5: PVOID, - Reserved6: ULONG, - Reserved7: PVOID, - Reserved8: ULONG, + IFEOKey: PVOID, + + // Versions: 6.0+ + + /// https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/crossprocessflags.htm + CrossProcessFlags: ULONG, + + // Versions: 6.0+ + union1: extern union { + KernelCallbackTable: PVOID, + UserSharedInfoPtr: PVOID, + }, + + // Versions: 5.1+ + SystemReserved: ULONG, + + // Versions: 5.1, (not 5.2, not 6.0), 6.1+ AtlThunkSListPtr32: ULONG, - Reserved9: [45]PVOID, - Reserved10: [96]BYTE, - PostProcessInitRoutine: PPS_POST_PROCESS_INIT_ROUTINE, - Reserved11: [128]BYTE, - Reserved12: [1]PVOID, + + // Versions: 6.1+ + ApiSetMap: PVOID, + + // Versions: all + TlsExpansionCounter: ULONG, + // note: there is padding here on 64 bit + TlsBitmap: PRTL_BITMAP, + TlsBitmapBits: [2]ULONG, + ReadOnlySharedMemoryBase: PVOID, + + // Versions: 1703+ + SharedData: PVOID, + + // Versions: all + ReadOnlyStaticServerData: *PVOID, + AnsiCodePageData: PVOID, + OemCodePageData: PVOID, + UnicodeCaseTableData: PVOID, + + // Versions: 3.51+ + NumberOfProcessors: ULONG, + NtGlobalFlag: ULONG, + + // Versions: all + CriticalSectionTimeout: LARGE_INTEGER, + + // End of Original PEB size + + // Fields appended in 3.51: + HeapSegmentReserve: ULONG_PTR, + HeapSegmentCommit: ULONG_PTR, + HeapDeCommitTotalFreeThreshold: ULONG_PTR, + HeapDeCommitFreeBlockThreshold: ULONG_PTR, + NumberOfHeaps: ULONG, + MaximumNumberOfHeaps: ULONG, + ProcessHeaps: *PVOID, + + // Fields appended in 4.0: + GdiSharedHandleTable: PVOID, + ProcessStarterHelper: PVOID, + GdiDCAttributeList: ULONG, + // note: there is padding here on 64 bit + LoaderLock: *RTL_CRITICAL_SECTION, + OSMajorVersion: ULONG, + OSMinorVersion: ULONG, + OSBuildNumber: USHORT, + OSCSDVersion: USHORT, + OSPlatformId: ULONG, + ImageSubSystem: ULONG, + ImageSubSystemMajorVersion: ULONG, + ImageSubSystemMinorVersion: ULONG, + // note: there is padding here on 64 bit + ActiveProcessAffinityMask: KAFFINITY, + GdiHandleBuffer: [switch (@sizeOf(usize)) { + 4 => 0x22, + 8 => 0x3C, + else => unreachable, + }]ULONG, + + // Fields appended in 5.0 (Windows 2000): + PostProcessInitRoutine: PVOID, + TlsExpansionBitmap: PRTL_BITMAP, + TlsExpansionBitmapBits: [32]ULONG, SessionId: ULONG, + // note: there is padding here on 64 bit + // Versions: 5.1+ + AppCompatFlags: ULARGE_INTEGER, + AppCompatFlagsUser: ULARGE_INTEGER, + ShimData: PVOID, + // Versions: 5.0+ + AppCompatInfo: PVOID, + CSDVersion: UNICODE_STRING, + + // Fields appended in 5.1 (Windows XP): + ActivationContextData: *const ACTIVATION_CONTEXT_DATA, + ProcessAssemblyStorageMap: *ASSEMBLY_STORAGE_MAP, + SystemDefaultActivationData: *const ACTIVATION_CONTEXT_DATA, + SystemAssemblyStorageMap: *ASSEMBLY_STORAGE_MAP, + MinimumStackCommit: ULONG_PTR, + + // Fields appended in 5.2 (Windows Server 2003): + FlsCallback: *FLS_CALLBACK_INFO, + FlsListHead: LIST_ENTRY, + FlsBitmap: PRTL_BITMAP, + FlsBitmapBits: [4]ULONG, + FlsHighIndex: ULONG, + + // Fields appended in 6.0 (Windows Vista): + WerRegistrationData: PVOID, + WerShipAssertPtr: PVOID, + + // Fields appended in 6.1 (Windows 7): + pUnused: PVOID, // previously pContextData + pImageHeaderHash: PVOID, + + /// TODO: https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/tracingflags.htm + TracingFlags: ULONG, + + // Fields appended in 6.2 (Windows 8): + CsrServerReadOnlySharedMemoryBase: ULONGLONG, + + // Fields appended in 1511: + TppWorkerpListLock: ULONG, + TppWorkerpList: LIST_ENTRY, + WaitOnAddressHashTable: [0x80]PVOID, + + // Fields appended in 1709: + TelemetryCoverageHeader: PVOID, + CloudFileFlags: ULONG, }; +// TODO: https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb_ldr_data.htm pub const PEB_LDR_DATA = extern struct { Reserved1: [8]BYTE, Reserved2: [3]PVOID, From 58222204353c052a62f3723f879dc16f2d608f8b Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 29 Feb 2020 14:19:13 +0100 Subject: [PATCH 80/99] Address review comments --- lib/std/zig/system.zig | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 333a5c3950..b04fb12885 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -181,7 +181,6 @@ pub const NativeTargetInfo = struct { ProcessFdQuotaExceeded, SystemFdQuotaExceeded, DeviceBusy, - Unexpected, }; /// Given a `CrossTarget`, which specifies in detail which parts of the target should be detected @@ -225,10 +224,9 @@ pub const NativeTargetInfo = struct { var version_info: std.os.windows.RTL_OSVERSIONINFOW = undefined; version_info.dwOSVersionInfoSize = @sizeOf(@TypeOf(version_info)); - const rc = std.os.windows.ntdll.RtlGetVersion(&version_info); - switch (rc) { + switch (std.os.windows.ntdll.RtlGetVersion(&version_info)) { .SUCCESS => {}, - else => return std.os.windows.unexpectedStatus(rc), + else => unreachable, } // Starting from the system infos build a NTDDI-like version @@ -245,15 +243,16 @@ pub const NativeTargetInfo = struct { // There's no other way to obtain this info beside // checking the build number against a known set of // values - for ([_]u32{ + const known_build_numbers = [_]u32{ 10240, 10586, 14393, 15063, 16299, 17134, 17763, 18362, 18363, - }) |build, i| { + }; + for (known_build_numbers) |build, i| { if (version_info.dwBuildNumber < build) break :subver @truncate(u8, i); } // Unknown subversion, the OS is too new... - break :subver 0; + break :subver @truncate(u8, known_build_numbers.len); } else 0; const version: u32 = @as(u32, os_ver) << 16 | @as(u32, sp_ver) << 8 | sub_ver; From d0c22619f59e41cb07d26d878ed1a9ec87e550e1 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Sun, 1 Mar 2020 02:10:29 +1100 Subject: [PATCH 81/99] Complete windows PEB_LDR_DATA definition --- lib/std/os/windows/bits.zig | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/std/os/windows/bits.zig b/lib/std/os/windows/bits.zig index 89e02fc7f1..923644b646 100644 --- a/lib/std/os/windows/bits.zig +++ b/lib/std/os/windows/bits.zig @@ -1312,11 +1312,37 @@ pub const PEB = extern struct { CloudFileFlags: ULONG, }; -// TODO: https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb_ldr_data.htm +/// The `PEB_LDR_DATA` structure is the main record of what modules are loaded in a process. +/// It is essentially the head of three double-linked lists of `LDR_DATA_TABLE_ENTRY` structures which each represent one loaded module. +/// +/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including: +/// - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb_ldr_data.htm pub const PEB_LDR_DATA = extern struct { - Reserved1: [8]BYTE, - Reserved2: [3]PVOID, + // Versions: 3.51 and higher + /// The size in bytes of the structure + Length: ULONG, + + /// TRUE if the structure is prepared. + Initialized: BOOLEAN, + + SsHandle: PVOID, + InLoadOrderModuleList: LIST_ENTRY, InMemoryOrderModuleList: LIST_ENTRY, + InInitializationOrderModuleList: LIST_ENTRY, + + // Versions: 5.1 and higher + + /// No known use of this field is known in Windows 8 and higher. + EntryInProgress: PVOID, + + // Versions: 6.0 from Windows Vista SP1, and higher + ShutdownInProgress: BOOLEAN, + + /// Though ShutdownThreadId is declared as a HANDLE, + /// it is indeed the thread ID as suggested by its name. + /// It is picked up from the UniqueThread member of the CLIENT_ID in the + /// TEB of the thread that asks to terminate the process. + ShutdownThreadId: HANDLE, }; pub const RTL_USER_PROCESS_PARAMETERS = extern struct { From 3c7f030a60b038045334788837f40bee0226109c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 29 Feb 2020 12:27:13 -0500 Subject: [PATCH 82/99] add CrossTarget.getObjectFormat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #4588 thanks Michaël Larouche for the suggested fix --- lib/std/target.zig | 12 ++++++++---- lib/std/zig/cross_target.zig | 4 ++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index 0082510505..c810c01ec3 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1014,18 +1014,22 @@ pub const Target = struct { return libPrefix_cpu_arch_abi(self.cpu.arch, self.abi); } - pub fn getObjectFormat(self: Target) ObjectFormat { - if (self.os.tag == .windows or self.os.tag == .uefi) { + pub fn getObjectFormatSimple(os_tag: Os.Tag, cpu_arch: Cpu.Arch) ObjectFormat { + if (os_tag == .windows or os_tag == .uefi) { return .coff; - } else if (self.isDarwin()) { + } else if (os_tag.isDarwin()) { return .macho; } - if (self.cpu.arch.isWasm()) { + if (cpu_arch.isWasm()) { return .wasm; } return .elf; } + pub fn getObjectFormat(self: Target) ObjectFormat { + return getObjectFormatSimple(self.os.tag, self.cpu.arch); + } + pub fn isMinGW(self: Target) bool { return self.os.tag == .windows and self.isGnu(); } diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 08a8d21fad..38ce6549f3 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -645,6 +645,10 @@ pub const CrossTarget = struct { self.glibc_version = SemVer{ .major = major, .minor = minor, .patch = patch }; } + pub fn getObjectFormat(self: CrossTarget) ObjectFormat { + return Target.getObjectFormatSimple(self.getOsTag(), self.getCpuArch()); + } + fn updateCpuFeatures(self: CrossTarget, set: *Target.Cpu.Feature.Set) void { set.removeFeatureSet(self.cpu_features_sub); set.addFeatureSet(self.cpu_features_add); From 278f0f8fa97610589dfb28c8fffedca7298e6715 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 29 Feb 2020 18:31:03 +0100 Subject: [PATCH 83/99] Change how the build-id is checked Address review comment by @rocksnest --- lib/std/zig/system.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index b04fb12885..e76d2eaca1 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -239,7 +239,7 @@ pub const NativeTargetInfo = struct { @intCast(u16, version_info.dwMajorVersion & 0xff) << 8 | @intCast(u16, version_info.dwMinorVersion & 0xff); const sp_ver: u8 = 0; - const sub_ver: u8 = if (os_ver >= 0xA000) subver: { + const sub_ver: u8 = if (os_ver >= 0x0A00) subver: { // There's no other way to obtain this info beside // checking the build number against a known set of // values @@ -247,12 +247,12 @@ pub const NativeTargetInfo = struct { 10240, 10586, 14393, 15063, 16299, 17134, 17763, 18362, 18363, }; + var last_idx: usize = 0; for (known_build_numbers) |build, i| { - if (version_info.dwBuildNumber < build) - break :subver @truncate(u8, i); + if (version_info.dwBuildNumber >= build) + last_idx = i; } - // Unknown subversion, the OS is too new... - break :subver @truncate(u8, known_build_numbers.len); + break :subver @truncate(u8, last_idx); } else 0; const version: u32 = @as(u32, os_ver) << 16 | @as(u32, sp_ver) << 8 | sub_ver; From 84549b39550014ca85d34c93381cadbd93080a0e Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 29 Feb 2020 21:46:07 +0100 Subject: [PATCH 84/99] target: Implement OS version detection for OSX Closes #4583 --- lib/std/os.zig | 3 +++ lib/std/zig/system.zig | 26 +++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 49e88bf9c7..24d78bec9a 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -2764,6 +2764,7 @@ pub const SysCtlError = error{ PermissionDenied, SystemResources, NameTooLong, + UnknownName, } || UnexpectedError; pub fn sysctl( @@ -2779,6 +2780,7 @@ pub fn sysctl( EFAULT => unreachable, EPERM => return error.PermissionDenied, ENOMEM => return error.SystemResources, + ENOENT => return error.UnknownName, else => |err| return unexpectedErrno(err), } } @@ -2795,6 +2797,7 @@ pub fn sysctlbynameC( EFAULT => unreachable, EPERM => return error.PermissionDenied, ENOMEM => return error.SystemResources, + ENOENT => return error.UnknownName, else => |err| return unexpectedErrno(err), } } diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index aa8def32a9..234c1a6550 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -224,7 +224,31 @@ pub const NativeTargetInfo = struct { // TODO Detect native operating system version. }, .macosx => { - // TODO Detect native operating system version. + var product_version: [32]u8 = undefined; + var size: usize = @sizeOf(@TypeOf(product_version)); + + // The osproductversion sysctl was introduced first with + // High Sierra, thankfully that's also the baseline that Zig + // supports + std.os.sysctlbynameC( + "kern.osproductversion", + &product_version[0], + &size, + null, + 0, + ) catch |err| switch (err) { + error.UnknownName => unreachable, + else => unreachable, + }; + + if (std.builtin.Version.parse(product_version[0..size])) |ver| { + os.version_range.semver.min = ver; + os.version_range.semver.max = ver; + } else |err| switch (err) { + error.Overflow => {}, + error.InvalidCharacter => {}, + error.InvalidVersion => {}, + } }, .freebsd => { // TODO Detect native operating system version. From 3b29a72b3b59803307e9040a6832e579901211f3 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 29 Feb 2020 23:08:57 +0100 Subject: [PATCH 85/99] Use .len instead of @sizeOf --- lib/std/zig/system.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 234c1a6550..7e23410fa8 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -225,7 +225,7 @@ pub const NativeTargetInfo = struct { }, .macosx => { var product_version: [32]u8 = undefined; - var size: usize = @sizeOf(@TypeOf(product_version)); + var size: usize = product_version.len; // The osproductversion sysctl was introduced first with // High Sierra, thankfully that's also the baseline that Zig From 845af5c55208753dc355fad734a8c4893338f8f6 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 29 Feb 2020 23:11:36 +0100 Subject: [PATCH 86/99] Handle one more error --- lib/std/thread.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/thread.zig b/lib/std/thread.zig index 55db9d1733..de9d580af7 100644 --- a/lib/std/thread.zig +++ b/lib/std/thread.zig @@ -382,7 +382,7 @@ pub const Thread = struct { var count_len: usize = @sizeOf(c_int); const name = if (comptime std.Target.current.isDarwin()) "hw.logicalcpu" else "hw.ncpu"; os.sysctlbynameC(name, &count, &count_len, null, 0) catch |err| switch (err) { - error.NameTooLong => unreachable, + error.NameTooLong, error.UnknownName => unreachable, else => |e| return e, }; return @intCast(usize, count); From 48cef8d5be24c40b4c845f6b07c78abd6c3d1b0d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 29 Feb 2020 21:31:03 -0500 Subject: [PATCH 87/99] refactor std.zig.system.NativeTargetInfo functions to make them a bit easier to test. --- lib/std/zig/system.zig | 65 ++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 7e23410fa8..37834b4412 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -399,17 +399,38 @@ pub const NativeTargetInfo = struct { return result; } + const env_file = std.fs.openFileAbsoluteC("/usr/bin/env", .{}) catch |err| switch (err) { + error.NoSpaceLeft => unreachable, + error.NameTooLong => unreachable, + error.PathAlreadyExists => unreachable, + error.SharingViolation => unreachable, + error.InvalidUtf8 => unreachable, + error.BadPathName => unreachable, + error.PipeBusy => unreachable, + + error.IsDir, + error.NotDir, + error.AccessDenied, + error.NoDevice, + error.FileNotFound, + error.FileTooBig, + error.Unexpected, + => return defaultAbiAndDynamicLinker(cpu, os, cross_target), + + else => |e| return e, + }; + defer env_file.close(); + // If Zig is statically linked, such as via distributed binary static builds, the above // trick won't work. The next thing we fall back to is the same thing, but for /usr/bin/env. // Since that path is hard-coded into the shebang line of many portable scripts, it's a // reasonably reliable path to check for. - return abiAndDynamicLinkerFromUsrBinEnv(cpu, os, ld_info_list, cross_target) catch |err| switch (err) { + return abiAndDynamicLinkerFromFile(env_file, cpu, os, ld_info_list, cross_target) catch |err| switch (err) { error.FileSystem, error.SystemResources, error.SymLinkLoop, error.ProcessFdQuotaExceeded, error.SystemFdQuotaExceeded, - error.DeviceBusy, => |e| return e, error.UnableToReadElfFile, @@ -418,7 +439,6 @@ pub const NativeTargetInfo = struct { error.InvalidElfEndian, error.InvalidElfFile, error.InvalidElfMagic, - error.UsrBinEnvNotAvailable, error.Unexpected, error.UnexpectedEndOfFile, error.NameTooLong, @@ -461,32 +481,15 @@ pub const NativeTargetInfo = struct { }; } - fn abiAndDynamicLinkerFromUsrBinEnv( + pub fn abiAndDynamicLinkerFromFile( + file: fs.File, cpu: Target.Cpu, os: Target.Os, ld_info_list: []const LdInfo, cross_target: CrossTarget, ) !NativeTargetInfo { - const env_file = std.fs.openFileAbsoluteC("/usr/bin/env", .{}) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - error.NameTooLong => unreachable, - error.PathAlreadyExists => unreachable, - error.SharingViolation => unreachable, - error.InvalidUtf8 => unreachable, - error.BadPathName => unreachable, - error.PipeBusy => unreachable, - - error.IsDir => return error.UsrBinEnvNotAvailable, - error.NotDir => return error.UsrBinEnvNotAvailable, - error.AccessDenied => return error.UsrBinEnvNotAvailable, - error.NoDevice => return error.UsrBinEnvNotAvailable, - error.FileNotFound => return error.UsrBinEnvNotAvailable, - error.FileTooBig => return error.UsrBinEnvNotAvailable, - - else => |e| return e, - }; var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined; - _ = try preadFull(env_file, &hdr_buf, 0, hdr_buf.len); + _ = try preadFull(file, &hdr_buf, 0, hdr_buf.len); const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf); const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf); if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; @@ -526,7 +529,7 @@ pub const NativeTargetInfo = struct { // Reserve some bytes so that we can deref the 64-bit struct fields // even when the ELF file is 32-bits. const ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr); - const ph_read_byte_len = try preadFull(env_file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize); + const ph_read_byte_len = try preadFull(file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize); var ph_buf_i: usize = 0; while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({ ph_i += 1; @@ -541,7 +544,7 @@ pub const NativeTargetInfo = struct { const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong; - _ = try preadFull(env_file, result.dynamic_linker.buffer[0..p_filesz], p_offset, p_filesz); + _ = try preadFull(file, result.dynamic_linker.buffer[0..p_filesz], p_offset, p_filesz); // PT_INTERP includes a null byte in p_filesz. const len = p_filesz - 1; // dynamic_linker.max_byte is "max", not "len". @@ -573,7 +576,7 @@ pub const NativeTargetInfo = struct { // even when the ELF file is 32-bits. const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn); const dyn_read_byte_len = try preadFull( - env_file, + file, dyn_buf[0 .. dyn_buf.len - dyn_reserve], dyn_off, dyn_size, @@ -617,14 +620,14 @@ pub const NativeTargetInfo = struct { var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined; if (sh_buf.len < shentsize) return error.InvalidElfFile; - _ = try preadFull(env_file, &sh_buf, str_section_off, shentsize); + _ = try preadFull(file, &sh_buf, str_section_off, shentsize); const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf)); const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf)); const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset); const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size); var strtab_buf: [4096:0]u8 = undefined; const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len); - const shstrtab_read_len = try preadFull(env_file, &strtab_buf, shstrtab_off, shstrtab_len); + const shstrtab_read_len = try preadFull(file, &strtab_buf, shstrtab_off, shstrtab_len); const shstrtab = strtab_buf[0..shstrtab_read_len]; const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum); @@ -634,7 +637,7 @@ pub const NativeTargetInfo = struct { // even when the ELF file is 32-bits. const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr); const sh_read_byte_len = try preadFull( - env_file, + file, sh_buf[0 .. sh_buf.len - sh_reserve], shoff, shentsize, @@ -667,7 +670,7 @@ pub const NativeTargetInfo = struct { if (dynstr) |ds| { const strtab_len = std.math.min(ds.size, strtab_buf.len); - const strtab_read_len = try preadFull(env_file, &strtab_buf, ds.offset, shstrtab_len); + const strtab_read_len = try preadFull(file, &strtab_buf, ds.offset, shstrtab_len); const strtab = strtab_buf[0..strtab_read_len]; // TODO this pointer cast should not be necessary const rpath_list = mem.toSliceConst(u8, @ptrCast([*:0]u8, strtab[rpoff..].ptr)); @@ -763,7 +766,7 @@ pub const NativeTargetInfo = struct { }; } - const LdInfo = struct { + pub const LdInfo = struct { ld: DynamicLinker, abi: Target.Abi, }; From 6fcf6716be3021d99d4972e8ca9b2139e1f8f40b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 29 Feb 2020 22:13:09 -0500 Subject: [PATCH 88/99] std.Thread.cpuCount on Windows uses the PEB rather than calling GetSystemInfo from kernel32.dll. Also remove OutOfMemory from the error set. --- lib/std/thread.zig | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/lib/std/thread.zig b/lib/std/thread.zig index de9d580af7..b2f8a44a47 100644 --- a/lib/std/thread.zig +++ b/lib/std/thread.zig @@ -1,5 +1,5 @@ -const builtin = @import("builtin"); const std = @import("std.zig"); +const builtin = std.builtin; const os = std.os; const mem = std.mem; const windows = std.os.windows; @@ -9,14 +9,14 @@ const assert = std.debug.assert; pub const Thread = struct { data: Data, - pub const use_pthreads = builtin.os.tag != .windows and builtin.link_libc; + pub const use_pthreads = std.Target.current.os.tag != .windows and builtin.link_libc; /// Represents a kernel thread handle. /// May be an integer or a pointer depending on the platform. /// On Linux and POSIX, this is the same as Id. pub const Handle = if (use_pthreads) c.pthread_t - else switch (builtin.os.tag) { + else switch (std.Target.current.os.tag) { .linux => i32, .windows => windows.HANDLE, else => void, @@ -25,7 +25,7 @@ pub const Thread = struct { /// Represents a unique ID per thread. /// May be an integer or pointer depending on the platform. /// On Linux and POSIX, this is the same as Handle. - pub const Id = switch (builtin.os.tag) { + pub const Id = switch (std.Target.current.os.tag) { .windows => windows.DWORD, else => Handle, }; @@ -35,7 +35,7 @@ pub const Thread = struct { handle: Thread.Handle, memory: []align(mem.page_size) u8, } - else switch (builtin.os.tag) { + else switch (std.Target.current.os.tag) { .linux => struct { handle: Thread.Handle, memory: []align(mem.page_size) u8, @@ -55,7 +55,7 @@ pub const Thread = struct { if (use_pthreads) { return c.pthread_self(); } else - return switch (builtin.os.tag) { + return switch (std.Target.current.os.tag) { .linux => os.linux.gettid(), .windows => windows.kernel32.GetCurrentThreadId(), else => @compileError("Unsupported OS"), @@ -83,7 +83,7 @@ pub const Thread = struct { else => unreachable, } os.munmap(self.data.memory); - } else switch (builtin.os.tag) { + } else switch (std.Target.current.os.tag) { .linux => { while (true) { const pid_value = @atomicLoad(i32, &self.data.handle, .SeqCst); @@ -150,7 +150,7 @@ pub const Thread = struct { const Context = @TypeOf(context); comptime assert(@typeInfo(@TypeOf(startFn)).Fn.args[0].arg_type.? == Context); - if (builtin.os.tag == .windows) { + if (std.Target.current.os.tag == .windows) { const WinThread = struct { const OuterContext = struct { thread: Thread, @@ -309,16 +309,16 @@ pub const Thread = struct { os.EINVAL => unreachable, else => return os.unexpectedErrno(@intCast(usize, err)), } - } else if (builtin.os.tag == .linux) { + } else if (std.Target.current.os.tag == .linux) { var flags: u32 = os.CLONE_VM | os.CLONE_FS | os.CLONE_FILES | os.CLONE_SIGHAND | os.CLONE_THREAD | os.CLONE_SYSVSEM | os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID | os.CLONE_DETACHED; var newtls: usize = undefined; // This structure is only needed when targeting i386 - var user_desc: if (builtin.arch == .i386) os.linux.user_desc else void = undefined; + var user_desc: if (std.Target.current.cpu.arch == .i386) os.linux.user_desc else void = undefined; if (os.linux.tls.tls_image) |tls_img| { - if (builtin.arch == .i386) { + if (std.Target.current.cpu.arch == .i386) { user_desc = os.linux.user_desc{ .entry_number = tls_img.gdt_entry_number, .base_addr = os.linux.tls.copyTLS(mmap_addr + tls_start_offset), @@ -362,21 +362,18 @@ pub const Thread = struct { } pub const CpuCountError = error{ - OutOfMemory, PermissionDenied, SystemResources, Unexpected, }; pub fn cpuCount() CpuCountError!usize { - if (builtin.os.tag == .linux) { + if (std.Target.current.os.tag == .linux) { const cpu_set = try os.sched_getaffinity(0); return @as(usize, os.CPU_COUNT(cpu_set)); // TODO should not need this usize cast } - if (builtin.os.tag == .windows) { - var system_info: windows.SYSTEM_INFO = undefined; - windows.kernel32.GetSystemInfo(&system_info); - return @intCast(usize, system_info.dwNumberOfProcessors); + if (std.Target.current.os.tag == .windows) { + return os.windows.peb().NumberOfProcessors; } var count: c_int = undefined; var count_len: usize = @sizeOf(c_int); From 22468121849e902a0ac2435af21fb9c773c7bcc4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 29 Feb 2020 22:20:44 -0500 Subject: [PATCH 89/99] use an explicit error set to fix a compilation error this function has conditionally compiled out code in it about looking for native glibc. --- lib/std/zig/system.zig | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 37834b4412..5767291de7 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -481,13 +481,30 @@ pub const NativeTargetInfo = struct { }; } + pub const AbiAndDynamicLinkerFromFileError = error{ + FileSystem, + SystemResources, + SymLinkLoop, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + UnableToReadElfFile, + InvalidElfClass, + InvalidElfVersion, + InvalidElfEndian, + InvalidElfFile, + InvalidElfMagic, + Unexpected, + UnexpectedEndOfFile, + NameTooLong, + }; + pub fn abiAndDynamicLinkerFromFile( file: fs.File, cpu: Target.Cpu, os: Target.Os, ld_info_list: []const LdInfo, cross_target: CrossTarget, - ) !NativeTargetInfo { + ) AbiAndDynamicLinkerFromFileError!NativeTargetInfo { var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined; _ = try preadFull(file, &hdr_buf, 0, hdr_buf.len); const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf); From f082e253b56b98a806e5ec047c5eb197b2c8ab88 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 1 Mar 2020 01:17:50 -0500 Subject: [PATCH 90/99] fix native OS version detection on macOS closes #4583 --- lib/std/zig/system.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 5767291de7..df50b4a4c2 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -232,7 +232,7 @@ pub const NativeTargetInfo = struct { // supports std.os.sysctlbynameC( "kern.osproductversion", - &product_version[0], + &product_version, &size, null, 0, @@ -241,7 +241,8 @@ pub const NativeTargetInfo = struct { else => unreachable, }; - if (std.builtin.Version.parse(product_version[0..size])) |ver| { + const string_version = product_version[0 .. size - 1 :0]; + if (std.builtin.Version.parse(string_version)) |ver| { os.version_range.semver.min = ver; os.version_range.semver.max = ver; } else |err| switch (err) { From b99c6d56da3d2db995c268fb07f48548ea6d1148 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 1 Mar 2020 03:27:09 -0500 Subject: [PATCH 91/99] stage1: fix compilation on 32-bit arm --- src/os.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/os.cpp b/src/os.cpp index 8e0bcc433b..f65a578e17 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1073,8 +1073,8 @@ static Error set_file_times(OsFile file, OsTimeStamp ts) { return ErrorNone; #else struct timespec times[2] = { - { ts.sec, ts.nsec }, - { ts.sec, ts.nsec }, + { (time_t)ts.sec, (time_t)ts.nsec }, + { (time_t)ts.sec, (time_t)ts.nsec }, }; if (futimens(file, times) == -1) { switch (errno) { From 0b0de22fd1b0391f0cf1c7d821afc6522041c699 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 27 Dec 2019 14:31:20 +1100 Subject: [PATCH 92/99] std: format contents of sentinel terminated many pointers std: add std.meta.Sentinel to get sentinel of a type --- lib/std/cstr.zig | 2 +- lib/std/fmt.zig | 8 ++++++-- lib/std/mem.zig | 23 ++++++++++++++++++----- lib/std/meta.zig | 26 ++++++++++++++++++++++++++ src-self-hosted/main.zig | 14 +++++++------- test/stage1/behavior/misc.zig | 2 +- 6 files changed, 59 insertions(+), 16 deletions(-) diff --git a/lib/std/cstr.zig b/lib/std/cstr.zig index 4057d4b62b..6b2407cb7d 100644 --- a/lib/std/cstr.zig +++ b/lib/std/cstr.zig @@ -28,7 +28,7 @@ test "cstr fns" { fn testCStrFnsImpl() void { testing.expect(cmp("aoeu", "aoez") == -1); - testing.expect(mem.len(u8, "123456789") == 9); + testing.expect(mem.len(u8, "123456789".*) == 9); } /// Returns a mutable, null-terminated slice with the same length as `slice`. diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index d093101646..97f2133d67 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -441,10 +441,14 @@ pub fn formatType( else => return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }), }, .Many, .C => { + if (ptr_info.sentinel) |sentinel| { + const slice = mem.pointerToSlice([:sentinel]const ptr_info.child, value); + return formatType(slice, fmt, options, context, Errors, output, max_depth); + } if (ptr_info.child == u8) { if (fmt.len > 0 and fmt[0] == 's') { - const len = mem.len(u8, value); - return formatText(value[0..len], fmt, options, context, Errors, output); + const slice = mem.pointerToSlice([:0]const u8, @as([*:0]const u8, value)); + return formatText(slice, fmt, options, context, Errors, output); } } return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); diff --git a/lib/std/mem.zig b/lib/std/mem.zig index a4b48bbc1c..e6ef7d6752 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -470,18 +470,31 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool { return true; } -pub fn len(comptime T: type, ptr: [*:0]const T) usize { +pub fn len(comptime T: type, ptr: var) usize { + const sentinel: T = comptime meta.Sentinel(@TypeOf(ptr)); var count: usize = 0; - while (ptr[count] != 0) : (count += 1) {} + while (ptr[count] != sentinel) : (count += 1) {} return count; } -pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T { - return ptr[0..len(T, ptr) :0]; +/// Given a sentintel-terminated pointer-to-many, find the sentintel and return a slice. +pub fn pointerToSlice(comptime T: type, ptr: blk: { + var info = @typeInfo(T).Pointer; + info.size = .Many; + break :blk @Type(std.builtin.TypeInfo{ .Pointer = info }); +}) T { + const sentinel = comptime meta.Sentinel(T); + return ptr[0..len(meta.Child(T), ptr) :sentinel]; } +/// Deprecated; use pointerToSlice instead +pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T { + return pointerToSlice([:0]const T, ptr); +} + +/// Deprecated; use pointerToSlice instead pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T { - return ptr[0..len(T, ptr) :0]; + return pointerToSlice([:0]T, ptr); } /// Returns true if all elements in a slice are equal to the scalar value provided diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 58fd6b9da7..65809abb5c 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -115,6 +115,32 @@ test "std.meta.Child" { testing.expect(Child(?u8) == u8); } +/// Given a type with a sentinel e.g. `[:0]u8`, returns the sentinel +pub fn Sentinel(comptime T: type) Child(T) { + // comptime asserts that ptr has a sentinel + switch (@typeInfo(T)) { + .Array => |arrayInfo| { + return comptime arrayInfo.sentinel.?; + }, + .Pointer => |ptrInfo| { + switch (ptrInfo.size) { + .Many, .Slice => { + return comptime ptrInfo.sentinel.?; + }, + else => {}, + } + }, + else => {}, + } + @compileError("not a sentinel type, found '" ++ @typeName(T) ++ "'"); +} + +test "std.meta.Sentinel" { + testing.expectEqual(@as(u8, 0), Sentinel([:0]u8)); + testing.expectEqual(@as(u8, 0), Sentinel([*:0]u8)); + testing.expectEqual(@as(u8, 0), Sentinel([5:0]u8)); +} + pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout { return switch (@typeInfo(T)) { .Struct => |info| info.layout, diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 264a896f46..4446a974d4 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -792,7 +792,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro } fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void { - try stdout.print("{}\n", .{std.mem.toSliceConst(u8, c.ZIG_VERSION_STRING)}); + try stdout.print("{}\n", .{c.ZIG_VERSION_STRING}); } fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void { @@ -863,12 +863,12 @@ fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void { \\ZIG_DIA_GUIDS_LIB {} \\ , .{ - std.mem.toSliceConst(u8, c.ZIG_CMAKE_BINARY_DIR), - std.mem.toSliceConst(u8, c.ZIG_CXX_COMPILER), - std.mem.toSliceConst(u8, c.ZIG_LLD_INCLUDE_PATH), - std.mem.toSliceConst(u8, c.ZIG_LLD_LIBRARIES), - std.mem.toSliceConst(u8, c.ZIG_LLVM_CONFIG_EXE), - std.mem.toSliceConst(u8, c.ZIG_DIA_GUIDS_LIB), + c.ZIG_CMAKE_BINARY_DIR, + c.ZIG_CXX_COMPILER, + c.ZIG_LLD_INCLUDE_PATH, + c.ZIG_LLD_LIBRARIES, + c.ZIG_LLVM_CONFIG_EXE, + c.ZIG_DIA_GUIDS_LIB, }); } diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index 681f5be500..e2513c3f4a 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -335,7 +335,7 @@ test "string concatenation" { comptime expect(@TypeOf(a) == *const [12:0]u8); comptime expect(@TypeOf(b) == *const [12:0]u8); - const len = mem.len(u8, b); + const len = b.len; const len_with_null = len + 1; { var i: u32 = 0; From 4505857e30233d87f9ece403458c0988e8c68493 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 1 Mar 2020 13:07:31 -0500 Subject: [PATCH 93/99] revert changes outside std.fmt --- lib/std/cstr.zig | 2 +- lib/std/mem.zig | 21 ++++----------------- test/stage1/behavior/misc.zig | 2 +- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/lib/std/cstr.zig b/lib/std/cstr.zig index 6b2407cb7d..4057d4b62b 100644 --- a/lib/std/cstr.zig +++ b/lib/std/cstr.zig @@ -28,7 +28,7 @@ test "cstr fns" { fn testCStrFnsImpl() void { testing.expect(cmp("aoeu", "aoez") == -1); - testing.expect(mem.len(u8, "123456789".*) == 9); + testing.expect(mem.len(u8, "123456789") == 9); } /// Returns a mutable, null-terminated slice with the same length as `slice`. diff --git a/lib/std/mem.zig b/lib/std/mem.zig index e6ef7d6752..a4b48bbc1c 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -470,31 +470,18 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool { return true; } -pub fn len(comptime T: type, ptr: var) usize { - const sentinel: T = comptime meta.Sentinel(@TypeOf(ptr)); +pub fn len(comptime T: type, ptr: [*:0]const T) usize { var count: usize = 0; - while (ptr[count] != sentinel) : (count += 1) {} + while (ptr[count] != 0) : (count += 1) {} return count; } -/// Given a sentintel-terminated pointer-to-many, find the sentintel and return a slice. -pub fn pointerToSlice(comptime T: type, ptr: blk: { - var info = @typeInfo(T).Pointer; - info.size = .Many; - break :blk @Type(std.builtin.TypeInfo{ .Pointer = info }); -}) T { - const sentinel = comptime meta.Sentinel(T); - return ptr[0..len(meta.Child(T), ptr) :sentinel]; -} - -/// Deprecated; use pointerToSlice instead pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T { - return pointerToSlice([:0]const T, ptr); + return ptr[0..len(T, ptr) :0]; } -/// Deprecated; use pointerToSlice instead pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T { - return pointerToSlice([:0]T, ptr); + return ptr[0..len(T, ptr) :0]; } /// Returns true if all elements in a slice are equal to the scalar value provided diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index e2513c3f4a..681f5be500 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -335,7 +335,7 @@ test "string concatenation" { comptime expect(@TypeOf(a) == *const [12:0]u8); comptime expect(@TypeOf(b) == *const [12:0]u8); - const len = b.len; + const len = mem.len(u8, b); const len_with_null = len + 1; { var i: u32 = 0; From 5b26128bacddf594dfe45958a236bfa2459f878b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 1 Mar 2020 13:07:55 -0500 Subject: [PATCH 94/99] add new functions to std.mem and deprecate others add std.mem.Span add std.mem.span add std.mem.length add std.mem.indexOfSentinel deprecate std.mem.len deprecate std.mem.toSlice deprecate std.mem.toSliceConst --- lib/std/builtin.zig | 2 + lib/std/mem.zig | 118 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index de37fda903..a4f0ef269f 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -185,6 +185,7 @@ pub const TypeInfo = union(enum) { child: type, is_allowzero: bool, + /// This field is an optional type. /// The type of the sentinel is the element type of the pointer, which is /// the value of the `child` field in this struct. However there is no way /// to refer to that type here, so we use `var`. @@ -206,6 +207,7 @@ pub const TypeInfo = union(enum) { len: comptime_int, child: type, + /// This field is an optional type. /// The type of the sentinel is the element type of the array, which is /// the value of the `child` field in this struct. However there is no way /// to refer to that type here, so we use `var`. diff --git a/lib/std/mem.zig b/lib/std/mem.zig index a4b48bbc1c..775e999e0b 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -333,8 +333,20 @@ pub fn zeroes(comptime T: type) T { } return array; }, - .Vector, .ErrorUnion, .ErrorSet, .Union, .Fn, .BoundFn, .Type, .NoReturn, .Undefined, .Opaque, .Frame, .AnyFrame, => { - @compileError("Can't set a "++ @typeName(T) ++" to zero."); + .Vector, + .ErrorUnion, + .ErrorSet, + .Union, + .Fn, + .BoundFn, + .Type, + .NoReturn, + .Undefined, + .Opaque, + .Frame, + .AnyFrame, + => { + @compileError("Can't set a " ++ @typeName(T) ++ " to zero."); }, } } @@ -470,20 +482,122 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool { return true; } +/// Deprecated. Use `length` or `indexOfSentinel`. pub fn len(comptime T: type, ptr: [*:0]const T) usize { var count: usize = 0; while (ptr[count] != 0) : (count += 1) {} return count; } +/// Deprecated. Use `span`. pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T { return ptr[0..len(T, ptr) :0]; } +/// Deprecated. Use `span`. pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T { return ptr[0..len(T, ptr) :0]; } +/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and +/// returns a slice. If there is a sentinel on the input type, there will be a +/// sentinel on the output type. The constness of the output type matches +/// the constness of the input type. `[*c]` pointers are assumed to be 0-terminated. +pub fn Span(comptime T: type) type { + var ptr_info = @typeInfo(T).Pointer; + switch (ptr_info.size) { + .One => switch (@typeInfo(ptr_info.child)) { + .Array => |info| { + ptr_info.child = info.child; + ptr_info.sentinel = info.sentinel; + }, + else => @compileError("invalid type given to std.mem.Span"), + }, + .C => { + ptr_info.sentinel = 0; + }, + .Many, .Slice => {}, + } + ptr_info.size = .Slice; + return @Type(std.builtin.TypeInfo{ .Pointer = ptr_info }); +} + +test "Span" { + testing.expect(Span(*[5]u16) == []u16); + testing.expect(Span(*const [5]u16) == []const u16); + testing.expect(Span([]u16) == []u16); + testing.expect(Span([]const u8) == []const u8); + testing.expect(Span([:1]u16) == [:1]u16); + testing.expect(Span([:1]const u8) == [:1]const u8); + testing.expect(Span([*:1]u16) == [:1]u16); + testing.expect(Span([*:1]const u8) == [:1]const u8); + testing.expect(Span([*c]u16) == [:0]u16); + testing.expect(Span([*c]const u8) == [:0]const u8); +} + +/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and +/// returns a slice. If there is a sentinel on the input type, there will be a +/// sentinel on the output type. The constness of the output type matches +/// the constness of the input type. +pub fn span(ptr: var) Span(@TypeOf(ptr)) { + const Result = Span(@TypeOf(ptr)); + const l = length(ptr); + if (@typeInfo(Result).Pointer.sentinel) |s| { + return ptr[0..l :s]; + } else { + return ptr[0..l]; + } +} + +test "span" { + var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; + const ptr = array[0..2 :3].ptr; + testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 })); + testing.expect(eql(u16, span(&array), &[_]u16{ 1, 2, 3, 4, 5 })); +} + +/// Takes a pointer to an array, an array, a sentinel-terminated pointer, +/// or a slice, and returns the length. +pub fn length(ptr: var) usize { + return switch (@typeInfo(@TypeOf(ptr))) { + .Array => |info| info.len, + .Pointer => |info| switch (info.size) { + .One => switch (@typeInfo(info.child)) { + .Array => |x| x.len, + else => @compileError("invalid type given to std.mem.length"), + }, + .Many => if (info.sentinel) |sentinel| + indexOfSentinel(info.child, sentinel, ptr) + else + @compileError("length of pointer with no sentinel"), + .C => indexOfSentinel(info.child, 0, ptr), + .Slice => ptr.len, + }, + else => @compileError("invalid type given to std.mem.length"), + }; +} + +test "length" { + testing.expect(length("aoeu") == 4); + + { + var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; + testing.expect(length(&array) == 5); + testing.expect(length(array[0..3]) == 3); + array[2] = 0; + const ptr = array[0..2 :0].ptr; + testing.expect(length(ptr) == 2); + } +} + +pub fn indexOfSentinel(comptime Elem: type, comptime sentinel: Elem, ptr: [*:sentinel]const Elem) usize { + var i: usize = 0; + while (ptr[i] != sentinel) { + i += 1; + } + return i; +} + /// Returns true if all elements in a slice are equal to the scalar value provided pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool { for (slice) |item| { From ef3d761da545a3a72928ed0e0ba3b749a4cb74d8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 1 Mar 2020 13:21:29 -0500 Subject: [PATCH 95/99] breaking: std.mem.len no longer takes a type argument also update fmt code to use std.mem.span. --- lib/std/cstr.zig | 2 +- lib/std/fmt.zig | 6 ++---- lib/std/mem.zig | 38 ++++++++++++++++----------------- lib/std/net.zig | 2 +- lib/std/os.zig | 2 +- lib/std/special/c.zig | 2 +- src-self-hosted/translate_c.zig | 2 +- test/stage1/behavior/misc.zig | 2 +- 8 files changed, 27 insertions(+), 29 deletions(-) diff --git a/lib/std/cstr.zig b/lib/std/cstr.zig index 4057d4b62b..9cb16b0ed3 100644 --- a/lib/std/cstr.zig +++ b/lib/std/cstr.zig @@ -28,7 +28,7 @@ test "cstr fns" { fn testCStrFnsImpl() void { testing.expect(cmp("aoeu", "aoez") == -1); - testing.expect(mem.len(u8, "123456789") == 9); + testing.expect(mem.len("123456789") == 9); } /// Returns a mutable, null-terminated slice with the same length as `slice`. diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 97f2133d67..3729b7dc87 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -442,13 +442,11 @@ pub fn formatType( }, .Many, .C => { if (ptr_info.sentinel) |sentinel| { - const slice = mem.pointerToSlice([:sentinel]const ptr_info.child, value); - return formatType(slice, fmt, options, context, Errors, output, max_depth); + return formatType(mem.span(value), fmt, options, context, Errors, output, max_depth); } if (ptr_info.child == u8) { if (fmt.len > 0 and fmt[0] == 's') { - const slice = mem.pointerToSlice([:0]const u8, @as([*:0]const u8, value)); - return formatText(slice, fmt, options, context, Errors, output); + return formatText(mem.span(value), fmt, options, context, Errors, output); } } return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 775e999e0b..a6c4d0057e 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -482,27 +482,21 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool { return true; } -/// Deprecated. Use `length` or `indexOfSentinel`. -pub fn len(comptime T: type, ptr: [*:0]const T) usize { - var count: usize = 0; - while (ptr[count] != 0) : (count += 1) {} - return count; -} - /// Deprecated. Use `span`. pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T { - return ptr[0..len(T, ptr) :0]; + return ptr[0..len(ptr) :0]; } /// Deprecated. Use `span`. pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T { - return ptr[0..len(T, ptr) :0]; + return ptr[0..len(ptr) :0]; } /// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and /// returns a slice. If there is a sentinel on the input type, there will be a /// sentinel on the output type. The constness of the output type matches -/// the constness of the input type. `[*c]` pointers are assumed to be 0-terminated. +/// the constness of the input type. `[*c]` pointers are assumed to be 0-terminated, +/// and assumed to not allow null. pub fn Span(comptime T: type) type { var ptr_info = @typeInfo(T).Pointer; switch (ptr_info.size) { @@ -515,6 +509,7 @@ pub fn Span(comptime T: type) type { }, .C => { ptr_info.sentinel = 0; + ptr_info.is_allowzero = false; }, .Many, .Slice => {}, } @@ -541,7 +536,7 @@ test "Span" { /// the constness of the input type. pub fn span(ptr: var) Span(@TypeOf(ptr)) { const Result = Span(@TypeOf(ptr)); - const l = length(ptr); + const l = len(ptr); if (@typeInfo(Result).Pointer.sentinel) |s| { return ptr[0..l :s]; } else { @@ -558,7 +553,7 @@ test "span" { /// Takes a pointer to an array, an array, a sentinel-terminated pointer, /// or a slice, and returns the length. -pub fn length(ptr: var) usize { +pub fn len(ptr: var) usize { return switch (@typeInfo(@TypeOf(ptr))) { .Array => |info| info.len, .Pointer => |info| switch (info.size) { @@ -577,16 +572,16 @@ pub fn length(ptr: var) usize { }; } -test "length" { - testing.expect(length("aoeu") == 4); +test "len" { + testing.expect(len("aoeu") == 4); { var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; - testing.expect(length(&array) == 5); - testing.expect(length(array[0..3]) == 3); + testing.expect(len(&array) == 5); + testing.expect(len(array[0..3]) == 3); array[2] = 0; const ptr = array[0..2 :0].ptr; - testing.expect(length(ptr) == 2); + testing.expect(len(ptr) == 2); } } @@ -1867,8 +1862,13 @@ fn SubArrayPtrReturnType(comptime T: type, comptime length: usize) type { return *[length]meta.Child(meta.Child(T)); } -///Given a pointer to an array, returns a pointer to a portion of that array, preserving constness. -pub fn subArrayPtr(ptr: var, comptime start: usize, comptime length: usize) SubArrayPtrReturnType(@TypeOf(ptr), length) { +/// Given a pointer to an array, returns a pointer to a portion of that array, preserving constness. +/// TODO this will be obsoleted by https://github.com/ziglang/zig/issues/863 +pub fn subArrayPtr( + ptr: var, + comptime start: usize, + comptime length: usize, +) SubArrayPtrReturnType(@TypeOf(ptr), length) { assert(start + length <= ptr.*.len); const ReturnType = SubArrayPtrReturnType(@TypeOf(ptr), length); diff --git a/lib/std/net.zig b/lib/std/net.zig index b54803cd39..6d0daefdc0 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -352,7 +352,7 @@ pub const Address = extern union { unreachable; } - const path_len = std.mem.len(u8, @ptrCast([*:0]const u8, &self.un.path)); + const path_len = std.mem.len(@ptrCast([*:0]const u8, &self.un.path)); return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len); }, else => unreachable, diff --git a/lib/std/os.zig b/lib/std/os.zig index 24d78bec9a..127ada8fe5 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1095,7 +1095,7 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std. pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) void { for (envp_buf) |env| { - const env_buf = if (env) |ptr| ptr[0 .. mem.len(u8, ptr) + 1] else break; + const env_buf = if (env) |ptr| ptr[0 .. mem.len(ptr) + 1] else break; allocator.free(env_buf); } allocator.free(envp_buf); diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index 1f11fabca0..e0c2636df1 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -47,7 +47,7 @@ fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.C) c_int { } fn strlen(s: [*:0]const u8) callconv(.C) usize { - return std.mem.len(u8, s); + return std.mem.len(s); } fn strncmp(_l: [*:0]const u8, _r: [*:0]const u8, _n: usize) callconv(.C) c_int { diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index b33c758c38..8efc0e3746 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -4849,7 +4849,7 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void { } const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc); - const slice = begin_c[0..mem.len(u8, begin_c)]; + const slice = begin_c[0..mem.len(begin_c)]; tok_list.shrink(0); var tokenizer = std.c.Tokenizer{ diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index 681f5be500..44a8334b5d 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -335,7 +335,7 @@ test "string concatenation" { comptime expect(@TypeOf(a) == *const [12:0]u8); comptime expect(@TypeOf(b) == *const [12:0]u8); - const len = mem.len(u8, b); + const len = mem.len(b); const len_with_null = len + 1; { var i: u32 = 0; From 00be934569d25e3b041091ff63a4cf6c456d1403 Mon Sep 17 00:00:00 2001 From: xackus <14938807+xackus@users.noreply.github.com> Date: Tue, 25 Feb 2020 21:29:56 +0100 Subject: [PATCH 96/99] short std.builtin enum literals in std lib --- lib/std/build.zig | 8 +-- lib/std/crypto/benchmark.zig | 2 +- lib/std/elf.zig | 4 +- lib/std/hash/auto_hash.zig | 8 +-- lib/std/hash/benchmark.zig | 2 +- lib/std/hash/cityhash.zig | 8 +-- lib/std/hash/murmur.zig | 16 ++--- lib/std/hash_map.zig | 2 +- lib/std/io.zig | 28 ++++---- lib/std/math/pow.zig | 2 +- lib/std/math/powi.zig | 2 +- lib/std/mem.zig | 96 ++++++++++++------------- lib/std/special/compiler_rt/ashlti3.zig | 2 +- lib/std/special/compiler_rt/ashrti3.zig | 2 +- lib/std/special/compiler_rt/lshrti3.zig | 2 +- lib/std/special/compiler_rt/multi3.zig | 2 +- lib/std/special/compiler_rt/udivmod.zig | 4 +- lib/std/testing.zig | 6 +- lib/std/valgrind.zig | 4 +- 19 files changed, 97 insertions(+), 103 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 3ca59ae4cb..ecf3930551 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1898,10 +1898,10 @@ pub const LibExeObjStep = struct { } switch (self.build_mode) { - builtin.Mode.Debug => {}, - builtin.Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable, - builtin.Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable, - builtin.Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable, + .Debug => {}, + .ReleaseSafe => zig_args.append("--release-safe") catch unreachable, + .ReleaseFast => zig_args.append("--release-fast") catch unreachable, + .ReleaseSmall => zig_args.append("--release-small") catch unreachable, } try zig_args.append("--cache-dir"); diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index 44326bfd97..0cc2c1d3ad 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -120,7 +120,7 @@ fn usage() void { } fn mode(comptime x: comptime_int) comptime_int { - return if (builtin.mode == builtin.Mode.Debug) x / 64 else x; + return if (builtin.mode == .Debug) x / 64 else x; } // TODO(#1358): Replace with builtin formatted padding when available. diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 99084f1897..fc57db7c98 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -370,8 +370,8 @@ pub const Elf = struct { }; elf.endian = switch (try in.readByte()) { - 1 => builtin.Endian.Little, - 2 => builtin.Endian.Big, + 1 => .Little, + 2 => .Big, else => return error.InvalidFormat, }; diff --git a/lib/std/hash/auto_hash.zig b/lib/std/hash/auto_hash.zig index 1bd7dfc05b..2c6b37e3db 100644 --- a/lib/std/hash/auto_hash.zig +++ b/lib/std/hash/auto_hash.zig @@ -25,13 +25,13 @@ pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void { const info = @typeInfo(@TypeOf(key)); switch (info.Pointer.size) { - builtin.TypeInfo.Pointer.Size.One => switch (strat) { + .One => switch (strat) { .Shallow => hash(hasher, @ptrToInt(key), .Shallow), .Deep => hash(hasher, key.*, .Shallow), .DeepRecursive => hash(hasher, key.*, .DeepRecursive), }, - builtin.TypeInfo.Pointer.Size.Slice => switch (strat) { + .Slice => switch (strat) { .Shallow => { hashPointer(hasher, key.ptr, .Shallow); hash(hasher, key.len, .Shallow); @@ -40,9 +40,7 @@ pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void { .DeepRecursive => hashArray(hasher, key, .DeepRecursive), }, - builtin.TypeInfo.Pointer.Size.Many, - builtin.TypeInfo.Pointer.Size.C, - => switch (strat) { + .Many, .C, => switch (strat) { .Shallow => hash(hasher, @ptrToInt(key), .Shallow), else => @compileError( \\ unknown-length pointers and C pointers cannot be hashed deeply. diff --git a/lib/std/hash/benchmark.zig b/lib/std/hash/benchmark.zig index ed1bab9d87..4762b65afb 100644 --- a/lib/std/hash/benchmark.zig +++ b/lib/std/hash/benchmark.zig @@ -168,7 +168,7 @@ fn usage() void { } fn mode(comptime x: comptime_int) comptime_int { - return if (builtin.mode == builtin.Mode.Debug) x / 64 else x; + return if (builtin.mode == .Debug) x / 64 else x; } pub fn main() !void { diff --git a/lib/std/hash/cityhash.zig b/lib/std/hash/cityhash.zig index 18b3abe16d..a717303090 100644 --- a/lib/std/hash/cityhash.zig +++ b/lib/std/hash/cityhash.zig @@ -11,7 +11,7 @@ pub const CityHash32 = struct { fn fetch32(ptr: [*]const u8) u32 { var v: u32 = undefined; @memcpy(@ptrCast([*]u8, &v), ptr, 4); - if (builtin.endian == builtin.Endian.Big) + if (builtin.endian == .Big) return @byteSwap(u32, v); return v; } @@ -174,7 +174,7 @@ pub const CityHash64 = struct { fn fetch32(ptr: [*]const u8) u32 { var v: u32 = undefined; @memcpy(@ptrCast([*]u8, &v), ptr, 4); - if (builtin.endian == builtin.Endian.Big) + if (builtin.endian == .Big) return @byteSwap(u32, v); return v; } @@ -182,7 +182,7 @@ pub const CityHash64 = struct { fn fetch64(ptr: [*]const u8) u64 { var v: u64 = undefined; @memcpy(@ptrCast([*]u8, &v), ptr, 8); - if (builtin.endian == builtin.Endian.Big) + if (builtin.endian == .Big) return @byteSwap(u64, v); return v; } @@ -369,7 +369,7 @@ fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 { key[i] = @intCast(u8, i); var h = hash_fn(key[0..i], 256 - i); - if (builtin.endian == builtin.Endian.Big) + if (builtin.endian == .Big) h = @byteSwap(@TypeOf(h), h); @memcpy(@ptrCast([*]u8, &hashes[i * hashbytes]), @ptrCast([*]u8, &h), hashbytes); } diff --git a/lib/std/hash/murmur.zig b/lib/std/hash/murmur.zig index 23b11ef284..96efc8b9c1 100644 --- a/lib/std/hash/murmur.zig +++ b/lib/std/hash/murmur.zig @@ -17,7 +17,7 @@ pub const Murmur2_32 = struct { var h1: u32 = seed ^ len; for (@ptrCast([*]align(1) const u32, str.ptr)[0..(len >> 2)]) |v| { var k1: u32 = v; - if (builtin.endian == builtin.Endian.Big) + if (builtin.endian == .Big) k1 = @byteSwap(u32, k1); k1 *%= m; k1 ^= k1 >> 24; @@ -102,7 +102,7 @@ pub const Murmur2_64 = struct { var h1: u64 = seed ^ (len *% m); for (@ptrCast([*]align(1) const u64, str.ptr)[0..@intCast(usize, len >> 3)]) |v| { var k1: u64 = v; - if (builtin.endian == builtin.Endian.Big) + if (builtin.endian == .Big) k1 = @byteSwap(u64, k1); k1 *%= m; k1 ^= k1 >> 47; @@ -115,7 +115,7 @@ pub const Murmur2_64 = struct { if (rest > 0) { var k1: u64 = 0; @memcpy(@ptrCast([*]u8, &k1), @ptrCast([*]const u8, &str[@intCast(usize, offset)]), @intCast(usize, rest)); - if (builtin.endian == builtin.Endian.Big) + if (builtin.endian == .Big) k1 = @byteSwap(u64, k1); h1 ^= k1; h1 *%= m; @@ -182,7 +182,7 @@ pub const Murmur3_32 = struct { var h1: u32 = seed; for (@ptrCast([*]align(1) const u32, str.ptr)[0..(len >> 2)]) |v| { var k1: u32 = v; - if (builtin.endian == builtin.Endian.Big) + if (builtin.endian == .Big) k1 = @byteSwap(u32, k1); k1 *%= c1; k1 = rotl32(k1, 15); @@ -294,7 +294,7 @@ fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 { key[i] = @truncate(u8, i); var h = hash_fn(key[0..i], 256 - i); - if (builtin.endian == builtin.Endian.Big) + if (builtin.endian == .Big) h = @byteSwap(@TypeOf(h), h); @memcpy(@ptrCast([*]u8, &hashes[i * hashbytes]), @ptrCast([*]u8, &h), hashbytes); } @@ -308,7 +308,7 @@ test "murmur2_32" { var v1: u64 = 0x1234567812345678; var v0le: u32 = v0; var v1le: u64 = v1; - if (builtin.endian == builtin.Endian.Big) { + if (builtin.endian == .Big) { v0le = @byteSwap(u32, v0le); v1le = @byteSwap(u64, v1le); } @@ -322,7 +322,7 @@ test "murmur2_64" { var v1: u64 = 0x1234567812345678; var v0le: u32 = v0; var v1le: u64 = v1; - if (builtin.endian == builtin.Endian.Big) { + if (builtin.endian == .Big) { v0le = @byteSwap(u32, v0le); v1le = @byteSwap(u64, v1le); } @@ -336,7 +336,7 @@ test "murmur3_32" { var v1: u64 = 0x1234567812345678; var v0le: u32 = v0; var v1le: u64 = v1; - if (builtin.endian == builtin.Endian.Big) { + if (builtin.endian == .Big) { v0le = @byteSwap(u32, v0le); v1le = @byteSwap(u64, v1le); } diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index 80ca218df6..4b3e082f0c 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -10,7 +10,7 @@ const Wyhash = std.hash.Wyhash; const Allocator = mem.Allocator; const builtin = @import("builtin"); -const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast; +const want_modification_safety = builtin.mode != .ReleaseFast; const debug_u32 = if (want_modification_safety) u32 else void; pub fn AutoHashMap(comptime K: type, comptime V: type) type { diff --git a/lib/std/io.zig b/lib/std/io.zig index 6a2a080ef5..d0bf26d548 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -348,11 +348,11 @@ pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type { const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count; const shift = u7_bit_count - n; switch (endian) { - builtin.Endian.Big => { + .Big => { out_buffer = @as(Buf, self.bit_buffer >> shift); self.bit_buffer <<= n; }, - builtin.Endian.Little => { + .Little => { const value = (self.bit_buffer << shift) >> shift; out_buffer = @as(Buf, value); self.bit_buffer >>= n; @@ -376,7 +376,7 @@ pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type { }; switch (endian) { - builtin.Endian.Big => { + .Big => { if (n >= u8_bit_count) { out_buffer <<= @intCast(u3, u8_bit_count - 1); out_buffer <<= 1; @@ -392,7 +392,7 @@ pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type { self.bit_buffer = @truncate(u7, next_byte << @intCast(u3, n - 1)); self.bit_count = shift; }, - builtin.Endian.Little => { + .Little => { if (n >= u8_bit_count) { out_buffer |= @as(Buf, next_byte) << @intCast(BufShift, out_bits.*); out_bits.* += u8_bit_count; @@ -666,8 +666,8 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type { const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count); var in_buffer = switch (endian) { - builtin.Endian.Big => buf_value << @intCast(BufShift, buf_bit_count - bits), - builtin.Endian.Little => buf_value, + .Big => buf_value << @intCast(BufShift, buf_bit_count - bits), + .Little => buf_value, }; var in_bits = bits; @@ -675,13 +675,13 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type { const bits_remaining = u8_bit_count - self.bit_count; const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining); switch (endian) { - builtin.Endian.Big => { + .Big => { const shift = @intCast(BufShift, high_byte_shift + self.bit_count); const v = @intCast(u8, in_buffer >> shift); self.bit_buffer |= v; in_buffer <<= n; }, - builtin.Endian.Little => { + .Little => { const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count); self.bit_buffer |= v; in_buffer >>= n; @@ -701,13 +701,13 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type { //copy bytes until we can't fill one anymore, then leave the rest in bit_buffer while (in_bits >= u8_bit_count) { switch (endian) { - builtin.Endian.Big => { + .Big => { const v = @intCast(u8, in_buffer >> high_byte_shift); try self.out_stream.writeByte(v); in_buffer <<= @intCast(u3, u8_bit_count - 1); in_buffer <<= 1; }, - builtin.Endian.Little => { + .Little => { const v = @truncate(u8, in_buffer); try self.out_stream.writeByte(v); in_buffer >>= @intCast(u3, u8_bit_count - 1); @@ -720,8 +720,8 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type { if (in_bits > 0) { self.bit_count = @intCast(u4, in_bits); self.bit_buffer = switch (endian) { - builtin.Endian.Big => @truncate(u8, in_buffer >> high_byte_shift), - builtin.Endian.Little => @truncate(u8, in_buffer), + .Big => @truncate(u8, in_buffer >> high_byte_shift), + .Little => @truncate(u8, in_buffer), }; } } @@ -858,10 +858,10 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, var result = @as(U, 0); for (buffer) |byte, i| { switch (endian) { - builtin.Endian.Big => { + .Big => { result = (result << u8_bit_count) | byte; }, - builtin.Endian.Little => { + .Little => { result |= @as(U, byte) << @intCast(Log2U, u8_bit_count * i); }, } diff --git a/lib/std/math/pow.zig b/lib/std/math/pow.zig index 4f623377e6..ac6aee4cce 100644 --- a/lib/std/math/pow.zig +++ b/lib/std/math/pow.zig @@ -32,7 +32,7 @@ const expect = std.testing.expect; /// - pow(-inf, y) = pow(-0, -y) /// - pow(x, y) = nan for finite x < 0 and finite non-integer y pub fn pow(comptime T: type, x: T, y: T) T { - if (@typeInfo(T) == builtin.TypeId.Int) { + if (@typeInfo(T) == .Int) { return math.powi(T, x, y) catch unreachable; } diff --git a/lib/std/math/powi.zig b/lib/std/math/powi.zig index d80700e5cd..ce3a3713e3 100644 --- a/lib/std/math/powi.zig +++ b/lib/std/math/powi.zig @@ -25,7 +25,7 @@ pub fn powi(comptime T: type, x: T, y: T) (error{ }!T) { const info = @typeInfo(T); - comptime assert(@typeInfo(T) == builtin.TypeId.Int); + comptime assert(@typeInfo(T) == .Int); // powi(x, +-0) = 1 for any x if (y == 0 or y == -0) { diff --git a/lib/std/mem.zig b/lib/std/mem.zig index a6c4d0057e..391d587dbc 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -746,12 +746,12 @@ test "mem.indexOf" { pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: builtin.Endian) ReturnType { var result: ReturnType = 0; switch (endian) { - builtin.Endian.Big => { + .Big => { for (bytes) |b| { result = (result << 8) | b; } }, - builtin.Endian.Little => { + .Little => { const ShiftType = math.Log2Int(ReturnType); for (bytes) |b, index| { result = result | (@as(ReturnType, b) << @intCast(ShiftType, index * 8)); @@ -779,13 +779,13 @@ pub fn readIntForeign(comptime T: type, bytes: *const [@divExact(T.bit_count, 8) } pub const readIntLittle = switch (builtin.endian) { - builtin.Endian.Little => readIntNative, - builtin.Endian.Big => readIntForeign, + .Little => readIntNative, + .Big => readIntForeign, }; pub const readIntBig = switch (builtin.endian) { - builtin.Endian.Little => readIntForeign, - builtin.Endian.Big => readIntNative, + .Little => readIntForeign, + .Big => readIntNative, }; /// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0 @@ -809,13 +809,13 @@ pub fn readIntSliceForeign(comptime T: type, bytes: []const u8) T { } pub const readIntSliceLittle = switch (builtin.endian) { - builtin.Endian.Little => readIntSliceNative, - builtin.Endian.Big => readIntSliceForeign, + .Little => readIntSliceNative, + .Big => readIntSliceForeign, }; pub const readIntSliceBig = switch (builtin.endian) { - builtin.Endian.Little => readIntSliceForeign, - builtin.Endian.Big => readIntSliceNative, + .Little => readIntSliceForeign, + .Big => readIntSliceNative, }; /// Reads an integer from memory with bit count specified by T. @@ -892,13 +892,13 @@ pub fn writeIntForeign(comptime T: type, buf: *[@divExact(T.bit_count, 8)]u8, va } pub const writeIntLittle = switch (builtin.endian) { - builtin.Endian.Little => writeIntNative, - builtin.Endian.Big => writeIntForeign, + .Little => writeIntNative, + .Big => writeIntForeign, }; pub const writeIntBig = switch (builtin.endian) { - builtin.Endian.Little => writeIntForeign, - builtin.Endian.Big => writeIntNative, + .Little => writeIntForeign, + .Big => writeIntNative, }; /// Writes an integer to memory, storing it in twos-complement. @@ -950,13 +950,13 @@ pub fn writeIntSliceBig(comptime T: type, buffer: []u8, value: T) void { } pub const writeIntSliceNative = switch (builtin.endian) { - builtin.Endian.Little => writeIntSliceLittle, - builtin.Endian.Big => writeIntSliceBig, + .Little => writeIntSliceLittle, + .Big => writeIntSliceBig, }; pub const writeIntSliceForeign = switch (builtin.endian) { - builtin.Endian.Little => writeIntSliceBig, - builtin.Endian.Big => writeIntSliceLittle, + .Little => writeIntSliceBig, + .Big => writeIntSliceLittle, }; /// Writes a twos-complement integer to memory, with the specified endianness. @@ -967,10 +967,10 @@ pub const writeIntSliceForeign = switch (builtin.endian) { /// use writeInt instead. pub fn writeIntSlice(comptime T: type, buffer: []u8, value: T, endian: builtin.Endian) void { comptime assert(T.bit_count % 8 == 0); - switch (endian) { - builtin.Endian.Little => return writeIntSliceLittle(T, buffer, value), - builtin.Endian.Big => return writeIntSliceBig(T, buffer, value), - } + return switch (endian) { + .Little => writeIntSliceLittle(T, buffer, value), + .Big => writeIntSliceBig(T, buffer, value), + }; } test "writeIntBig and writeIntLittle" { @@ -1506,54 +1506,54 @@ test "rotate" { /// Converts a little-endian integer to host endianness. pub fn littleToNative(comptime T: type, x: T) T { return switch (builtin.endian) { - builtin.Endian.Little => x, - builtin.Endian.Big => @byteSwap(T, x), + .Little => x, + .Big => @byteSwap(T, x), }; } /// Converts a big-endian integer to host endianness. pub fn bigToNative(comptime T: type, x: T) T { return switch (builtin.endian) { - builtin.Endian.Little => @byteSwap(T, x), - builtin.Endian.Big => x, + .Little => @byteSwap(T, x), + .Big => x, }; } /// Converts an integer from specified endianness to host endianness. pub fn toNative(comptime T: type, x: T, endianness_of_x: builtin.Endian) T { return switch (endianness_of_x) { - builtin.Endian.Little => littleToNative(T, x), - builtin.Endian.Big => bigToNative(T, x), + .Little => littleToNative(T, x), + .Big => bigToNative(T, x), }; } /// Converts an integer which has host endianness to the desired endianness. pub fn nativeTo(comptime T: type, x: T, desired_endianness: builtin.Endian) T { return switch (desired_endianness) { - builtin.Endian.Little => nativeToLittle(T, x), - builtin.Endian.Big => nativeToBig(T, x), + .Little => nativeToLittle(T, x), + .Big => nativeToBig(T, x), }; } /// Converts an integer which has host endianness to little endian. pub fn nativeToLittle(comptime T: type, x: T) T { return switch (builtin.endian) { - builtin.Endian.Little => x, - builtin.Endian.Big => @byteSwap(T, x), + .Little => x, + .Big => @byteSwap(T, x), }; } /// Converts an integer which has host endianness to big endian. pub fn nativeToBig(comptime T: type, x: T) T { return switch (builtin.endian) { - builtin.Endian.Little => @byteSwap(T, x), - builtin.Endian.Big => x, + .Little => @byteSwap(T, x), + .Big => x, }; } fn AsBytesReturnType(comptime P: type) type { if (comptime !trait.isSingleItemPtr(P)) - @compileError("expected single item " ++ "pointer, passed " ++ @typeName(P)); + @compileError("expected single item pointer, passed " ++ @typeName(P)); const size = @as(usize, @sizeOf(meta.Child(P))); const alignment = comptime meta.alignment(P); @@ -1578,8 +1578,8 @@ pub fn asBytes(ptr: var) AsBytesReturnType(@TypeOf(ptr)) { test "asBytes" { const deadbeef = @as(u32, 0xDEADBEEF); const deadbeef_bytes = switch (builtin.endian) { - builtin.Endian.Big => "\xDE\xAD\xBE\xEF", - builtin.Endian.Little => "\xEF\xBE\xAD\xDE", + .Big => "\xDE\xAD\xBE\xEF", + .Little => "\xEF\xBE\xAD\xDE", }; testing.expect(eql(u8, asBytes(&deadbeef), deadbeef_bytes)); @@ -1617,21 +1617,21 @@ pub fn toBytes(value: var) [@sizeOf(@TypeOf(value))]u8 { test "toBytes" { var my_bytes = toBytes(@as(u32, 0x12345678)); switch (builtin.endian) { - builtin.Endian.Big => testing.expect(eql(u8, &my_bytes, "\x12\x34\x56\x78")), - builtin.Endian.Little => testing.expect(eql(u8, &my_bytes, "\x78\x56\x34\x12")), + .Big => testing.expect(eql(u8, &my_bytes, "\x12\x34\x56\x78")), + .Little => testing.expect(eql(u8, &my_bytes, "\x78\x56\x34\x12")), } my_bytes[0] = '\x99'; switch (builtin.endian) { - builtin.Endian.Big => testing.expect(eql(u8, &my_bytes, "\x99\x34\x56\x78")), - builtin.Endian.Little => testing.expect(eql(u8, &my_bytes, "\x99\x56\x34\x12")), + .Big => testing.expect(eql(u8, &my_bytes, "\x99\x34\x56\x78")), + .Little => testing.expect(eql(u8, &my_bytes, "\x99\x56\x34\x12")), } } fn BytesAsValueReturnType(comptime T: type, comptime B: type) type { const size = @as(usize, @sizeOf(T)); - if (comptime !trait.is(builtin.TypeId.Pointer)(B) or + if (comptime !trait.is(.Pointer)(B) or (meta.Child(B) != [size]u8 and meta.Child(B) != [size:0]u8)) { @compileError("expected *[N]u8 " ++ ", passed " ++ @typeName(B)); @@ -1651,15 +1651,15 @@ pub fn bytesAsValue(comptime T: type, bytes: var) BytesAsValueReturnType(T, @Typ test "bytesAsValue" { const deadbeef = @as(u32, 0xDEADBEEF); const deadbeef_bytes = switch (builtin.endian) { - builtin.Endian.Big => "\xDE\xAD\xBE\xEF", - builtin.Endian.Little => "\xEF\xBE\xAD\xDE", + .Big => "\xDE\xAD\xBE\xEF", + .Little => "\xEF\xBE\xAD\xDE", }; testing.expect(deadbeef == bytesAsValue(u32, deadbeef_bytes).*); var codeface_bytes: [4]u8 = switch (builtin.endian) { - builtin.Endian.Big => "\xC0\xDE\xFA\xCE", - builtin.Endian.Little => "\xCE\xFA\xDE\xC0", + .Big => "\xC0\xDE\xFA\xCE", + .Little => "\xCE\xFA\xDE\xC0", }.*; var codeface = bytesAsValue(u32, &codeface_bytes); testing.expect(codeface.* == 0xC0DEFACE); @@ -1692,8 +1692,8 @@ pub fn bytesToValue(comptime T: type, bytes: var) T { } test "bytesToValue" { const deadbeef_bytes = switch (builtin.endian) { - builtin.Endian.Big => "\xDE\xAD\xBE\xEF", - builtin.Endian.Little => "\xEF\xBE\xAD\xDE", + .Big => "\xDE\xAD\xBE\xEF", + .Little => "\xEF\xBE\xAD\xDE", }; const deadbeef = bytesToValue(u32, deadbeef_bytes); diff --git a/lib/std/special/compiler_rt/ashlti3.zig b/lib/std/special/compiler_rt/ashlti3.zig index 211515f9dd..e3bc60bbb9 100644 --- a/lib/std/special/compiler_rt/ashlti3.zig +++ b/lib/std/special/compiler_rt/ashlti3.zig @@ -24,7 +24,7 @@ const twords = extern union { all: i128, s: S, - const S = if (builtin.endian == builtin.Endian.Little) + const S = if (builtin.endian == .Little) struct { low: u64, high: u64, diff --git a/lib/std/special/compiler_rt/ashrti3.zig b/lib/std/special/compiler_rt/ashrti3.zig index 1bcd40d2e4..1cbe24fdeb 100644 --- a/lib/std/special/compiler_rt/ashrti3.zig +++ b/lib/std/special/compiler_rt/ashrti3.zig @@ -25,7 +25,7 @@ const twords = extern union { all: i128, s: S, - const S = if (builtin.endian == builtin.Endian.Little) + const S = if (builtin.endian == .Little) struct { low: i64, high: i64, diff --git a/lib/std/special/compiler_rt/lshrti3.zig b/lib/std/special/compiler_rt/lshrti3.zig index 043be5e2a2..e1c2bb5bd3 100644 --- a/lib/std/special/compiler_rt/lshrti3.zig +++ b/lib/std/special/compiler_rt/lshrti3.zig @@ -24,7 +24,7 @@ const twords = extern union { all: i128, s: S, - const S = if (builtin.endian == builtin.Endian.Little) + const S = if (builtin.endian == .Little) struct { low: u64, high: u64, diff --git a/lib/std/special/compiler_rt/multi3.zig b/lib/std/special/compiler_rt/multi3.zig index f46bd55421..eba58c45fc 100644 --- a/lib/std/special/compiler_rt/multi3.zig +++ b/lib/std/special/compiler_rt/multi3.zig @@ -45,7 +45,7 @@ const twords = extern union { all: i128, s: S, - const S = if (builtin.endian == builtin.Endian.Little) + const S = if (builtin.endian == .Little) struct { low: u64, high: u64, diff --git a/lib/std/special/compiler_rt/udivmod.zig b/lib/std/special/compiler_rt/udivmod.zig index 1cf2589b16..9f0fb7d50f 100644 --- a/lib/std/special/compiler_rt/udivmod.zig +++ b/lib/std/special/compiler_rt/udivmod.zig @@ -2,8 +2,8 @@ const builtin = @import("builtin"); const is_test = builtin.is_test; const low = switch (builtin.endian) { - builtin.Endian.Big => 1, - builtin.Endian.Little => 0, + .Big => 1, + .Little => 0, }; const high = 1 - low; diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 398a71ff37..4f527ba700 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -63,15 +63,11 @@ pub fn expectEqual(expected: var, actual: @TypeOf(expected)) void { .Pointer => |pointer| { switch (pointer.size) { - .One, - .Many, - .C, - => { + .One, .Many, .C => { if (actual != expected) { std.debug.panic("expected {*}, found {*}", .{ expected, actual }); } }, - .Slice => { if (actual.ptr != expected.ptr) { std.debug.panic("expected slice ptr {}, found {}", .{ expected.ptr, actual.ptr }); diff --git a/lib/std/valgrind.zig b/lib/std/valgrind.zig index fe1eb65b7a..38c4a491e0 100644 --- a/lib/std/valgrind.zig +++ b/lib/std/valgrind.zig @@ -8,7 +8,7 @@ pub fn doClientRequest(default: usize, request: usize, a1: usize, a2: usize, a3: } switch (builtin.arch) { - builtin.Arch.i386 => { + .i386 => { return asm volatile ( \\ roll $3, %%edi ; roll $13, %%edi \\ roll $29, %%edi ; roll $19, %%edi @@ -19,7 +19,7 @@ pub fn doClientRequest(default: usize, request: usize, a1: usize, a2: usize, a3: : "cc", "memory" ); }, - builtin.Arch.x86_64 => { + .x86_64 => { return asm volatile ( \\ rolq $3, %%rdi ; rolq $13, %%rdi \\ rolq $61, %%rdi ; rolq $51, %%rdi From a48cd1cf585a0f69f381a61ac43678c64e4a9a81 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Mon, 2 Mar 2020 11:44:21 +0200 Subject: [PATCH 97/99] Allow wrapping in VDSO lookup --- lib/std/os/linux/vdso.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/os/linux/vdso.zig b/lib/std/os/linux/vdso.zig index 868eb26c69..dc056ddba4 100644 --- a/lib/std/os/linux/vdso.zig +++ b/lib/std/os/linux/vdso.zig @@ -22,7 +22,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { }) { const this_ph = @intToPtr(*elf.Phdr, ph_addr); switch (this_ph.p_type) { - elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr, + elf.PT_LOAD => base = vdso_addr +% this_ph.p_offset -% this_ph.p_vaddr, elf.PT_DYNAMIC => maybe_dynv = @intToPtr([*]usize, vdso_addr + this_ph.p_offset), else => {}, } @@ -40,7 +40,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { { var i: usize = 0; while (dynv[i] != 0) : (i += 2) { - const p = base + dynv[i + 1]; + const p = base +% dynv[i + 1]; switch (dynv[i]) { elf.DT_STRTAB => maybe_strings = @intToPtr([*]u8, p), elf.DT_SYMTAB => maybe_syms = @intToPtr([*]elf.Sym, p), @@ -71,7 +71,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { if (!checkver(maybe_verdef.?, versym[i], vername, strings)) continue; } - return base + syms[i].st_value; + return base +% syms[i].st_value; } return 0; From d7579a2fcb0edaa1f6036101d00316abccac0c7e Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Tue, 3 Mar 2020 00:03:19 -0800 Subject: [PATCH 98/99] Fix std.os.unlinkatW for absolute paths RootDirectory can't be set in ObjectAttributes if ObjectName is an absolute path. Fixes #4606 --- lib/std/os.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 127ada8fe5..fbfef4ac4c 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1438,7 +1438,7 @@ pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatEr var attr = w.OBJECT_ATTRIBUTES{ .Length = @sizeOf(w.OBJECT_ATTRIBUTES), - .RootDirectory = dirfd, + .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd, .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. .ObjectName = &nt_name, .SecurityDescriptor = null, From 755941830555b7423903053742fcc21d49cd3923 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 3 Mar 2020 12:41:11 +0200 Subject: [PATCH 99/99] Added comment justifying wrapping operations --- lib/std/os/linux/vdso.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/std/os/linux/vdso.zig b/lib/std/os/linux/vdso.zig index dc056ddba4..beb98a063d 100644 --- a/lib/std/os/linux/vdso.zig +++ b/lib/std/os/linux/vdso.zig @@ -22,6 +22,10 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { }) { const this_ph = @intToPtr(*elf.Phdr, ph_addr); switch (this_ph.p_type) { + // On WSL1 as well as older kernels, the VDSO ELF image is pre-linked in the upper half + // of the memory space (e.g. p_vaddr = 0xffffffffff700000 on WSL1). + // Wrapping operations are used on this line as well as subsequent calculations relative to base + // (lines 47, 78) to ensure no overflow check is tripped. elf.PT_LOAD => base = vdso_addr +% this_ph.p_offset -% this_ph.p_vaddr, elf.PT_DYNAMIC => maybe_dynv = @intToPtr([*]usize, vdso_addr + this_ph.p_offset), else => {},