| author | |
| committer | |
| log | 41723f842c713af5e78a120c374732671a3317c2 |
| tree | 07940697cfb840e5b294ad764c816b37eead8e0f |
| parent | 9de0f900e1a80554ac72c8675fc2896977f4930b |
| parent | 2ec9a11646c792a046b4601e0b99f8e182416a6c |
9 files changed, 638 insertions(+), 52 deletions(-)
CMakeLists.txt+2| ... | ... | @@ -444,6 +444,7 @@ set(ZIG_STD_FILES |
| 444 | 444 | "c/index.zig" |
| 445 | 445 | "c/linux.zig" |
| 446 | 446 | "c/windows.zig" |
| 447 | "coff.zig" | |
| 447 | 448 | "crypto/blake2.zig" |
| 448 | 449 | "crypto/hmac.zig" |
| 449 | 450 | "crypto/index.zig" |
| ... | ... | @@ -583,6 +584,7 @@ set(ZIG_STD_FILES |
| 583 | 584 | "os/windows/user32.zig" |
| 584 | 585 | "os/windows/util.zig" |
| 585 | 586 | "os/zen.zig" |
| 587 | "pdb.zig" | |
| 586 | 588 | "rand/index.zig" |
| 587 | 589 | "rand/ziggurat.zig" |
| 588 | 590 | "segmented_list.zig" |
std/coff.zig created+238| ... | ... | @@ -0,0 +1,238 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const std = @import("index.zig"); | |
| 3 | const io = std.io; | |
| 4 | const mem = std.mem; | |
| 5 | const os = std.os; | |
| 6 | ||
| 7 | const ArrayList = std.ArrayList; | |
| 8 | ||
| 9 | // CoffHeader.machine values | |
| 10 | // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx | |
| 11 | const IMAGE_FILE_MACHINE_I386 = 0x014c; | |
| 12 | const IMAGE_FILE_MACHINE_IA64 = 0x0200; | |
| 13 | const IMAGE_FILE_MACHINE_AMD64 = 0x8664; | |
| 14 | ||
| 15 | // OptionalHeader.magic values | |
| 16 | // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx | |
| 17 | const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b; | |
| 18 | const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b; | |
| 19 | ||
| 20 | const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; | |
| 21 | const DEBUG_DIRECTORY = 6; | |
| 22 | ||
| 23 | pub const CoffError = error { | |
| 24 | InvalidPEMagic, | |
| 25 | InvalidPEHeader, | |
| 26 | InvalidMachine, | |
| 27 | MissingCoffSection, | |
| 28 | }; | |
| 29 | ||
| 30 | pub const Coff = struct { | |
| 31 | in_file: os.File, | |
| 32 | allocator: *mem.Allocator, | |
| 33 | ||
| 34 | coff_header: CoffHeader, | |
| 35 | pe_header: OptionalHeader, | |
| 36 | sections: ArrayList(Section), | |
| 37 | ||
| 38 | guid: [16]u8, | |
| 39 | age: u32, | |
| 40 | ||
| 41 | pub fn loadHeader(self: *Coff) !void { | |
| 42 | const pe_pointer_offset = 0x3C; | |
| 43 | ||
| 44 | var file_stream = io.FileInStream.init(&self.in_file); | |
| 45 | const in = &file_stream.stream; | |
| 46 | ||
| 47 | var magic: [2]u8 = undefined; | |
| 48 | try in.readNoEof(magic[0..]); | |
| 49 | if (!mem.eql(u8, magic, "MZ")) | |
| 50 | return error.InvalidPEMagic; | |
| 51 | ||
| 52 | // Seek to PE File Header (coff header) | |
| 53 | try self.in_file.seekTo(pe_pointer_offset); | |
| 54 | const pe_magic_offset = try in.readIntLe(u32); | |
| 55 | try self.in_file.seekTo(pe_magic_offset); | |
| 56 | ||
| 57 | var pe_header_magic: [4]u8 = undefined; | |
| 58 | try in.readNoEof(pe_header_magic[0..]); | |
| 59 | if (!mem.eql(u8, pe_header_magic, []u8{'P', 'E', 0, 0})) | |
| 60 | return error.InvalidPEHeader; | |
| 61 | ||
| 62 | self.coff_header = CoffHeader { | |
| 63 | .machine = try in.readIntLe(u16), | |
| 64 | .number_of_sections = try in.readIntLe(u16), | |
| 65 | .timedate_stamp = try in.readIntLe(u32), | |
| 66 | .pointer_to_symbol_table = try in.readIntLe(u32), | |
| 67 | .number_of_symbols = try in.readIntLe(u32), | |
| 68 | .size_of_optional_header = try in.readIntLe(u16), | |
| 69 | .characteristics = try in.readIntLe(u16), | |
| 70 | }; | |
| 71 | ||
| 72 | switch (self.coff_header.machine) { | |
| 73 | IMAGE_FILE_MACHINE_I386, | |
| 74 | IMAGE_FILE_MACHINE_AMD64, | |
| 75 | IMAGE_FILE_MACHINE_IA64 | |
| 76 | => {}, | |
| 77 | else => return error.InvalidMachine, | |
| 78 | } | |
| 79 | ||
| 80 | try self.loadOptionalHeader(&file_stream); | |
| 81 | } | |
| 82 | ||
| 83 | fn loadOptionalHeader(self: *Coff, file_stream: *io.FileInStream) !void { | |
| 84 | const in = &file_stream.stream; | |
| 85 | self.pe_header.magic = try in.readIntLe(u16); | |
| 86 | std.debug.warn("reading pe optional\n"); | |
| 87 | // For now we're only interested in finding the reference to the .pdb, | |
| 88 | // so we'll skip most of this header, which size is different in 32 | |
| 89 | // 64 bits by the way. | |
| 90 | var skip_size: u16 = undefined; | |
| 91 | if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { | |
| 92 | skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 18 * @sizeOf(u32); | |
| 93 | } | |
| 94 | else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { | |
| 95 | skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 12 * @sizeOf(u32) + 5 * @sizeOf(u64); | |
| 96 | } | |
| 97 | else | |
| 98 | return error.InvalidPEMagic; | |
| 99 | ||
| 100 | std.debug.warn("skipping {}\n", skip_size); | |
| 101 | try self.in_file.seekForward(skip_size); | |
| 102 | ||
| 103 | const number_of_rva_and_sizes = try in.readIntLe(u32); | |
| 104 | //std.debug.warn("indicating {} data dirs\n", number_of_rva_and_sizes); | |
| 105 | if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES) | |
| 106 | return error.InvalidPEHeader; | |
| 107 | ||
| 108 | for (self.pe_header.data_directory) |*data_dir| { | |
| 109 | data_dir.* = OptionalHeader.DataDirectory { | |
| 110 | .virtual_address = try in.readIntLe(u32), | |
| 111 | .size = try in.readIntLe(u32), | |
| 112 | }; | |
| 113 | //std.debug.warn("data_dir @ {x}, size {}\n", data_dir.virtual_address, data_dir.size); | |
| 114 | } | |
| 115 | std.debug.warn("loaded data directories\n"); | |
| 116 | } | |
| 117 | ||
| 118 | pub fn getPdbPath(self: *Coff, buffer: []u8) !usize { | |
| 119 | try self.loadSections(); | |
| 120 | const header = (self.getSection(".rdata") orelse return error.MissingCoffSection).header; | |
| 121 | ||
| 122 | // The linker puts a chunk that contains the .pdb path right after the | |
| 123 | // debug_directory. | |
| 124 | const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY]; | |
| 125 | const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data; | |
| 126 | std.debug.warn("file offset {x}\n", file_offset); | |
| 127 | try self.in_file.seekTo(file_offset + debug_dir.size); | |
| 128 | ||
| 129 | var file_stream = io.FileInStream.init(&self.in_file); | |
| 130 | const in = &file_stream.stream; | |
| 131 | ||
| 132 | var cv_signature: [4]u8 = undefined; // CodeView signature | |
| 133 | try in.readNoEof(cv_signature[0..]); | |
| 134 | // 'RSDS' indicates PDB70 format, used by lld. | |
| 135 | if (!mem.eql(u8, cv_signature, "RSDS")) | |
| 136 | return error.InvalidPEMagic; | |
| 137 | std.debug.warn("cv_signature {}\n", cv_signature); | |
| 138 | try in.readNoEof(self.guid[0..]); | |
| 139 | self.age = try in.readIntLe(u32); | |
| 140 | ||
| 141 | // Finally read the null-terminated string. | |
| 142 | var byte = try in.readByte(); | |
| 143 | var i: usize = 0; | |
| 144 | while (byte != 0 and i < buffer.len) : (i += 1) { | |
| 145 | buffer[i] = byte; | |
| 146 | byte = try in.readByte(); | |
| 147 | } | |
| 148 | ||
| 149 | if (byte != 0 and i == buffer.len) | |
| 150 | return error.NameTooLong; | |
| 151 | ||
| 152 | return i; | |
| 153 | } | |
| 154 | ||
| 155 | pub fn loadSections(self: *Coff) !void { | |
| 156 | if (self.sections.len != 0) | |
| 157 | return; | |
| 158 | ||
| 159 | self.sections = ArrayList(Section).init(self.allocator); | |
| 160 | ||
| 161 | var file_stream = io.FileInStream.init(&self.in_file); | |
| 162 | const in = &file_stream.stream; | |
| 163 | ||
| 164 | var name: [8]u8 = undefined; | |
| 165 | ||
| 166 | var i: u16 = 0; | |
| 167 | while (i < self.coff_header.number_of_sections) : (i += 1) { | |
| 168 | try in.readNoEof(name[0..]); | |
| 169 | try self.sections.append(Section { | |
| 170 | .header = SectionHeader { | |
| 171 | .name = name, | |
| 172 | .misc = SectionHeader.Misc { .physical_address = try in.readIntLe(u32) }, | |
| 173 | .virtual_address = try in.readIntLe(u32), | |
| 174 | .size_of_raw_data = try in.readIntLe(u32), | |
| 175 | .pointer_to_raw_data = try in.readIntLe(u32), | |
| 176 | .pointer_to_relocations = try in.readIntLe(u32), | |
| 177 | .pointer_to_line_numbers = try in.readIntLe(u32), | |
| 178 | .number_of_relocations = try in.readIntLe(u16), | |
| 179 | .number_of_line_numbers = try in.readIntLe(u16), | |
| 180 | .characteristics = try in.readIntLe(u32), | |
| 181 | }, | |
| 182 | }); | |
| 183 | } | |
| 184 | std.debug.warn("loaded {} sections\n", self.coff_header.number_of_sections); | |
| 185 | } | |
| 186 | ||
| 187 | pub fn getSection(self: *Coff, comptime name: []const u8) ?*Section { | |
| 188 | for (self.sections.toSlice()) |*sec| { | |
| 189 | if (mem.eql(u8, sec.header.name[0..name.len], name)) { | |
| 190 | return sec; | |
| 191 | } | |
| 192 | } | |
| 193 | return null; | |
| 194 | } | |
| 195 | ||
| 196 | }; | |
| 197 | ||
| 198 | const CoffHeader = struct { | |
| 199 | machine: u16, | |
| 200 | number_of_sections: u16, | |
| 201 | timedate_stamp: u32, | |
| 202 | pointer_to_symbol_table: u32, | |
| 203 | number_of_symbols: u32, | |
| 204 | size_of_optional_header: u16, | |
| 205 | characteristics: u16 | |
| 206 | }; | |
| 207 | ||
| 208 | const OptionalHeader = struct { | |
| 209 | const DataDirectory = struct { | |
| 210 | virtual_address: u32, | |
| 211 | size: u32 | |
| 212 | }; | |
| 213 | ||
| 214 | magic: u16, | |
| 215 | data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory, | |
| 216 | }; | |
| 217 | ||
| 218 | const Section = struct { | |
| 219 | header: SectionHeader, | |
| 220 | }; | |
| 221 | ||
| 222 | const SectionHeader = struct { | |
| 223 | const Misc = union { | |
| 224 | physical_address: u32, | |
| 225 | virtual_size: u32 | |
| 226 | }; | |
| 227 | ||
| 228 | name: [8]u8, | |
| 229 | misc: Misc, | |
| 230 | virtual_address: u32, | |
| 231 | size_of_raw_data: u32, | |
| 232 | pointer_to_raw_data: u32, | |
| 233 | pointer_to_relocations: u32, | |
| 234 | pointer_to_line_numbers: u32, | |
| 235 | number_of_relocations: u16, | |
| 236 | number_of_line_numbers: u16, | |
| 237 | characteristics: u32, | |
| 238 | }; | |
| \ No newline at end of file |
std/debug/index.zig+52-10| ... | ... | @@ -4,8 +4,11 @@ const mem = std.mem; |
| 4 | 4 | const io = std.io; |
| 5 | 5 | const os = std.os; |
| 6 | 6 | const elf = std.elf; |
| 7 | const macho = std.macho; | |
| 8 | 7 | const DW = std.dwarf; |
| 8 | const macho = std.macho; | |
| 9 | const coff = std.coff; | |
| 10 | const pdb = std.pdb; | |
| 11 | const windows = os.windows; | |
| 9 | 12 | const ArrayList = std.ArrayList; |
| 10 | 13 | const builtin = @import("builtin"); |
| 11 | 14 | |
| ... | ... | @@ -228,14 +231,19 @@ pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: us |
| 228 | 231 | switch (builtin.os) { |
| 229 | 232 | builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color), |
| 230 | 233 | builtin.Os.linux => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color), |
| 231 | builtin.Os.windows => { | |
| 232 | // TODO https://github.com/ziglang/zig/issues/721 | |
| 233 | return error.UnsupportedOperatingSystem; | |
| 234 | }, | |
| 234 | builtin.Os.windows => return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color), | |
| 235 | 235 | else => return error.UnsupportedOperatingSystem, |
| 236 | 236 | } |
| 237 | 237 | } |
| 238 | 238 | |
| 239 | fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { | |
| 240 | const base_address = @ptrToInt(windows.GetModuleHandleW(null)); // returned HMODULE points to our executable file in memory | |
| 241 | const relative_address = address - base_address; | |
| 242 | std.debug.warn("{x} - {x} => {x}\n", address, base_address, relative_address); | |
| 243 | try di.pdb.getSourceLine(relative_address); | |
| 244 | return error.UnsupportedDebugInfo; | |
| 245 | } | |
| 246 | ||
| 239 | 247 | fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol { |
| 240 | 248 | var min: usize = 0; |
| 241 | 249 | var max: usize = symbols.len - 1; // Exclude sentinel. |
| ... | ... | @@ -372,14 +380,44 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { |
| 372 | 380 | switch (builtin.os) { |
| 373 | 381 | builtin.Os.linux => return openSelfDebugInfoLinux(allocator), |
| 374 | 382 | builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator), |
| 375 | builtin.Os.windows => { | |
| 376 | // TODO: https://github.com/ziglang/zig/issues/721 | |
| 377 | return error.UnsupportedOperatingSystem; | |
| 378 | }, | |
| 383 | builtin.Os.windows => return openSelfDebugInfoWindows(allocator), | |
| 379 | 384 | else => return error.UnsupportedOperatingSystem, |
| 380 | 385 | } |
| 381 | 386 | } |
| 382 | 387 | |
| 388 | fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { | |
| 389 | var coff_file: coff.Coff = undefined; | |
| 390 | coff_file.in_file = try os.openSelfExe(); | |
| 391 | coff_file.allocator = allocator; | |
| 392 | defer coff_file.in_file.close(); | |
| 393 | ||
| 394 | try coff_file.loadHeader(); | |
| 395 | ||
| 396 | var path: [windows.MAX_PATH]u8 = undefined; | |
| 397 | const len = try coff_file.getPdbPath(path[0..]); | |
| 398 | std.debug.warn("pdb path {}\n", path[0..len]); | |
| 399 | ||
| 400 | var di = DebugInfo{ | |
| 401 | .pdb = undefined, | |
| 402 | }; | |
| 403 | ||
| 404 | try di.pdb.openFile(allocator, path[0..len]); | |
| 405 | ||
| 406 | var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; | |
| 407 | std.debug.warn("pdb real filepos {}\n", pdb_stream.getFilePos()); | |
| 408 | const version = try pdb_stream.stream.readIntLe(u32); | |
| 409 | const signature = try pdb_stream.stream.readIntLe(u32); | |
| 410 | const age = try pdb_stream.stream.readIntLe(u32); | |
| 411 | var guid: [16]u8 = undefined; | |
| 412 | try pdb_stream.stream.readNoEof(guid[0..]); | |
| 413 | if (!mem.eql(u8, coff_file.guid, guid) or coff_file.age != age) | |
| 414 | return error.InvalidDebugInfo; | |
| 415 | std.debug.warn("v {} s {} a {}\n", version, signature, age); | |
| 416 | // We validated the executable and pdb match. | |
| 417 | ||
| 418 | return di; | |
| 419 | } | |
| 420 | ||
| 383 | 421 | fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo { |
| 384 | 422 | var di = DebugInfo{ |
| 385 | 423 | .self_exe_file = undefined, |
| ... | ... | @@ -578,7 +616,10 @@ pub const DebugInfo = switch (builtin.os) { |
| 578 | 616 | return self.ofiles.allocator; |
| 579 | 617 | } |
| 580 | 618 | }, |
| 581 | else => struct { | |
| 619 | builtin.Os.windows => struct { | |
| 620 | pdb: pdb.Pdb, | |
| 621 | }, | |
| 622 | builtin.Os.linux => struct { | |
| 582 | 623 | self_exe_file: os.File, |
| 583 | 624 | elf: elf.Elf, |
| 584 | 625 | debug_info: *elf.SectionHeader, |
| ... | ... | @@ -604,6 +645,7 @@ pub const DebugInfo = switch (builtin.os) { |
| 604 | 645 | self.elf.close(); |
| 605 | 646 | } |
| 606 | 647 | }, |
| 648 | else => @compileError("Unsupported OS"), | |
| 607 | 649 | }; |
| 608 | 650 | |
| 609 | 651 | const PcRange = struct { |
std/index.zig+4| ... | ... | @@ -15,6 +15,7 @@ pub const atomic = @import("atomic/index.zig"); |
| 15 | 15 | pub const base64 = @import("base64.zig"); |
| 16 | 16 | pub const build = @import("build.zig"); |
| 17 | 17 | pub const c = @import("c/index.zig"); |
| 18 | pub const coff = @import("coff.zig"); | |
| 18 | 19 | pub const crypto = @import("crypto/index.zig"); |
| 19 | 20 | pub const cstr = @import("cstr.zig"); |
| 20 | 21 | pub const debug = @import("debug/index.zig"); |
| ... | ... | @@ -33,6 +34,7 @@ pub const math = @import("math/index.zig"); |
| 33 | 34 | pub const mem = @import("mem.zig"); |
| 34 | 35 | pub const net = @import("net.zig"); |
| 35 | 36 | pub const os = @import("os/index.zig"); |
| 37 | pub const pdb = @import("pdb.zig"); | |
| 36 | 38 | pub const rand = @import("rand/index.zig"); |
| 37 | 39 | pub const rb = @import("rb.zig"); |
| 38 | 40 | pub const sort = @import("sort.zig"); |
| ... | ... | @@ -56,6 +58,7 @@ test "std" { |
| 56 | 58 | _ = @import("base64.zig"); |
| 57 | 59 | _ = @import("build.zig"); |
| 58 | 60 | _ = @import("c/index.zig"); |
| 61 | _ = @import("coff.zig"); | |
| 59 | 62 | _ = @import("crypto/index.zig"); |
| 60 | 63 | _ = @import("cstr.zig"); |
| 61 | 64 | _ = @import("debug/index.zig"); |
| ... | ... | @@ -74,6 +77,7 @@ test "std" { |
| 74 | 77 | _ = @import("heap.zig"); |
| 75 | 78 | _ = @import("os/index.zig"); |
| 76 | 79 | _ = @import("rand/index.zig"); |
| 80 | _ = @import("pdb.zig"); | |
| 77 | 81 | _ = @import("sort.zig"); |
| 78 | 82 | _ = @import("unicode.zig"); |
| 79 | 83 | _ = @import("zig/index.zig"); |
std/os/file.zig+39-24| ... | ... | @@ -48,18 +48,23 @@ pub const File = struct { |
| 48 | 48 | return openReadC(&path_c); |
| 49 | 49 | } |
| 50 | 50 | if (is_windows) { |
| 51 | const handle = try os.windowsOpen( | |
| 52 | path, | |
| 53 | windows.GENERIC_READ, | |
| 54 | windows.FILE_SHARE_READ, | |
| 55 | windows.OPEN_EXISTING, | |
| 56 | windows.FILE_ATTRIBUTE_NORMAL, | |
| 57 | ); | |
| 58 | return openHandle(handle); | |
| 51 | const path_w = try windows_util.sliceToPrefixedFileW(path); | |
| 52 | return openReadW(&path_w); | |
| 59 | 53 | } |
| 60 | 54 | @compileError("Unsupported OS"); |
| 61 | 55 | } |
| 62 | 56 | |
| 57 | pub fn openReadW(path_w: [*]const u16) OpenError!File { | |
| 58 | const handle = try os.windowsOpenW( | |
| 59 | path_w, | |
| 60 | windows.GENERIC_READ, | |
| 61 | windows.FILE_SHARE_READ, | |
| 62 | windows.OPEN_EXISTING, | |
| 63 | windows.FILE_ATTRIBUTE_NORMAL, | |
| 64 | ); | |
| 65 | return openHandle(handle); | |
| 66 | } | |
| 67 | ||
| 63 | 68 | /// Calls `openWriteMode` with os.File.default_mode for the mode. |
| 64 | 69 | pub fn openWrite(path: []const u8) OpenError!File { |
| 65 | 70 | return openWriteMode(path, os.File.default_mode); |
| ... | ... | @@ -74,19 +79,24 @@ pub const File = struct { |
| 74 | 79 | const fd = try os.posixOpen(path, flags, file_mode); |
| 75 | 80 | return openHandle(fd); |
| 76 | 81 | } else if (is_windows) { |
| 77 | const handle = try os.windowsOpen( | |
| 78 | path, | |
| 79 | windows.GENERIC_WRITE, | |
| 80 | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, | |
| 81 | windows.CREATE_ALWAYS, | |
| 82 | windows.FILE_ATTRIBUTE_NORMAL, | |
| 83 | ); | |
| 84 | return openHandle(handle); | |
| 82 | const path_w = try windows_util.sliceToPrefixedFileW(path); | |
| 83 | return openWriteModeW(&path_w, file_mode); | |
| 85 | 84 | } else { |
| 86 | 85 | @compileError("TODO implement openWriteMode for this OS"); |
| 87 | 86 | } |
| 88 | 87 | } |
| 89 | 88 | |
| 89 | pub fn openWriteModeW(path_w: [*]const u16, file_mode: Mode) OpenError!File { | |
| 90 | const handle = try os.windowsOpenW( | |
| 91 | path_w, | |
| 92 | windows.GENERIC_WRITE, | |
| 93 | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, | |
| 94 | windows.CREATE_ALWAYS, | |
| 95 | windows.FILE_ATTRIBUTE_NORMAL, | |
| 96 | ); | |
| 97 | return openHandle(handle); | |
| 98 | } | |
| 99 | ||
| 90 | 100 | /// If the path does not exist it will be created. |
| 91 | 101 | /// If a file already exists in the destination this returns OpenError.PathAlreadyExists |
| 92 | 102 | /// Call close to clean up. |
| ... | ... | @@ -96,19 +106,24 @@ pub const File = struct { |
| 96 | 106 | const fd = try os.posixOpen(path, flags, file_mode); |
| 97 | 107 | return openHandle(fd); |
| 98 | 108 | } else if (is_windows) { |
| 99 | const handle = try os.windowsOpen( | |
| 100 | path, | |
| 101 | windows.GENERIC_WRITE, | |
| 102 | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, | |
| 103 | windows.CREATE_NEW, | |
| 104 | windows.FILE_ATTRIBUTE_NORMAL, | |
| 105 | ); | |
| 106 | return openHandle(handle); | |
| 109 | const path_w = try windows_util.sliceToPrefixedFileW(path); | |
| 110 | return openWriteNoClobberW(&path_w, file_mode); | |
| 107 | 111 | } else { |
| 108 | 112 | @compileError("TODO implement openWriteMode for this OS"); |
| 109 | 113 | } |
| 110 | 114 | } |
| 111 | 115 | |
| 116 | pub fn openWriteNoClobberW(path_w: [*]const u16, file_mode: Mode) OpenError!File { | |
| 117 | const handle = try os.windowsOpenW( | |
| 118 | path_w, | |
| 119 | windows.GENERIC_WRITE, | |
| 120 | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, | |
| 121 | windows.CREATE_NEW, | |
| 122 | windows.FILE_ATTRIBUTE_NORMAL, | |
| 123 | ); | |
| 124 | return openHandle(handle); | |
| 125 | } | |
| 126 | ||
| 112 | 127 | pub fn openHandle(handle: os.FileHandle) File { |
| 113 | 128 | return File{ .handle = handle }; |
| 114 | 129 | } |
std/os/index.zig+22-13| ... | ... | @@ -57,6 +57,7 @@ pub const windowsWaitSingle = windows_util.windowsWaitSingle; |
| 57 | 57 | pub const windowsWrite = windows_util.windowsWrite; |
| 58 | 58 | pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; |
| 59 | 59 | pub const windowsOpen = windows_util.windowsOpen; |
| 60 | pub const windowsOpenW = windows_util.windowsOpenW; | |
| 60 | 61 | pub const windowsLoadDll = windows_util.windowsLoadDll; |
| 61 | 62 | pub const windowsUnloadDll = windows_util.windowsUnloadDll; |
| 62 | 63 | pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; |
| ... | ... | @@ -2103,15 +2104,33 @@ pub fn openSelfExe() !os.File { |
| 2103 | 2104 | buf[self_exe_path.len] = 0; |
| 2104 | 2105 | return os.File.openReadC(self_exe_path.ptr); |
| 2105 | 2106 | }, |
| 2107 | Os.windows => { | |
| 2108 | var buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | |
| 2109 | const wide_slice = try selfExePathW(&buf); | |
| 2110 | return os.File.openReadW(wide_slice.ptr); | |
| 2111 | }, | |
| 2106 | 2112 | else => @compileError("Unsupported OS"), |
| 2107 | 2113 | } |
| 2108 | 2114 | } |
| 2109 | 2115 | |
| 2110 | 2116 | test "openSelfExe" { |
| 2111 | 2117 | switch (builtin.os) { |
| 2112 | Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(), | |
| 2113 | else => return error.SkipZigTest, // Unsupported OS | |
| 2118 | Os.linux, Os.macosx, Os.ios, Os.windows => (try openSelfExe()).close(), | |
| 2119 | else => return error.SkipZigTest, // Unsupported OS. | |
| 2120 | } | |
| 2121 | } | |
| 2122 | ||
| 2123 | pub fn selfExePathW(out_buffer: *[windows_util.PATH_MAX_WIDE]u16) ![]u16 { | |
| 2124 | const casted_len = @intCast(windows.DWORD, out_buffer.len); // TODO shouldn't need this cast | |
| 2125 | const rc = windows.GetModuleFileNameW(null, out_buffer, casted_len); | |
| 2126 | assert(rc <= out_buffer.len); | |
| 2127 | if (rc == 0) { | |
| 2128 | const err = windows.GetLastError(); | |
| 2129 | switch (err) { | |
| 2130 | else => return unexpectedErrorWindows(err), | |
| 2131 | } | |
| 2114 | 2132 | } |
| 2133 | return out_buffer[0..rc]; | |
| 2115 | 2134 | } |
| 2116 | 2135 | |
| 2117 | 2136 | /// Get the path to the current executable. |
| ... | ... | @@ -2128,17 +2147,7 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 2128 | 2147 | switch (builtin.os) { |
| 2129 | 2148 | Os.linux => return readLink(out_buffer, "/proc/self/exe"), |
| 2130 | 2149 | Os.windows => { |
| 2131 | var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | |
| 2132 | const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast | |
| 2133 | const rc = windows.GetModuleFileNameW(null, &utf16le_buf, casted_len); | |
| 2134 | assert(rc <= utf16le_buf.len); | |
| 2135 | if (rc == 0) { | |
| 2136 | const err = windows.GetLastError(); | |
| 2137 | switch (err) { | |
| 2138 | else => return unexpectedErrorWindows(err), | |
| 2139 | } | |
| 2140 | } | |
| 2141 | const utf16le_slice = utf16le_buf[0..rc]; | |
| 2150 | const utf16le_slice = try selfExePathW(&utf16le_buf); | |
| 2142 | 2151 | // Trust that Windows gives us valid UTF-16LE. |
| 2143 | 2152 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; |
| 2144 | 2153 | return out_buffer[0..end_index]; |
std/os/windows/kernel32.zig+2| ... | ... | @@ -92,6 +92,8 @@ pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR |
| 92 | 92 | pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD; |
| 93 | 93 | pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD; |
| 94 | 94 | |
| 95 | pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE; | |
| 96 | ||
| 95 | 97 | pub extern "kernel32" stdcallcc fn GetLastError() DWORD; |
| 96 | 98 | |
| 97 | 99 | pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( |
std/os/windows/util.zig+14-5| ... | ... | @@ -118,16 +118,14 @@ pub const OpenError = error{ |
| 118 | 118 | Unexpected, |
| 119 | 119 | }; |
| 120 | 120 | |
| 121 | pub fn windowsOpen( | |
| 122 | file_path: []const u8, | |
| 121 | pub fn windowsOpenW( | |
| 122 | file_path_w: [*]const u16, | |
| 123 | 123 | desired_access: windows.DWORD, |
| 124 | 124 | share_mode: windows.DWORD, |
| 125 | 125 | creation_disposition: windows.DWORD, |
| 126 | 126 | flags_and_attrs: windows.DWORD, |
| 127 | 127 | ) OpenError!windows.HANDLE { |
| 128 | const file_path_w = try sliceToPrefixedFileW(file_path); | |
| 129 | ||
| 130 | const result = windows.CreateFileW(&file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); | |
| 128 | const result = windows.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); | |
| 131 | 129 | |
| 132 | 130 | if (result == windows.INVALID_HANDLE_VALUE) { |
| 133 | 131 | const err = windows.GetLastError(); |
| ... | ... | @@ -146,6 +144,17 @@ pub fn windowsOpen( |
| 146 | 144 | return result; |
| 147 | 145 | } |
| 148 | 146 | |
| 147 | pub fn windowsOpen( | |
| 148 | file_path: []const u8, | |
| 149 | desired_access: windows.DWORD, | |
| 150 | share_mode: windows.DWORD, | |
| 151 | creation_disposition: windows.DWORD, | |
| 152 | flags_and_attrs: windows.DWORD, | |
| 153 | ) OpenError!windows.HANDLE { | |
| 154 | const file_path_w = try sliceToPrefixedFileW(file_path); | |
| 155 | return windowsOpenW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs); | |
| 156 | } | |
| 157 | ||
| 149 | 158 | /// Caller must free result. |
| 150 | 159 | pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u8 { |
| 151 | 160 | // count bytes needed |
std/pdb.zig created+265| ... | ... | @@ -0,0 +1,265 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const std = @import("index.zig"); | |
| 3 | const io = std.io; | |
| 4 | const math = std.math; | |
| 5 | const mem = std.mem; | |
| 6 | const os = std.os; | |
| 7 | const warn = std.debug.warn; | |
| 8 | ||
| 9 | const ArrayList = std.ArrayList; | |
| 10 | ||
| 11 | pub const PdbError = error { | |
| 12 | InvalidPdbMagic, | |
| 13 | CorruptedFile, | |
| 14 | }; | |
| 15 | ||
| 16 | pub const StreamType = enum(u16) { | |
| 17 | Pdb = 1, | |
| 18 | Tpi = 2, | |
| 19 | Dbi = 3, | |
| 20 | Ipi = 4, | |
| 21 | }; | |
| 22 | ||
| 23 | pub const Pdb = struct { | |
| 24 | in_file: os.File, | |
| 25 | allocator: *mem.Allocator, | |
| 26 | ||
| 27 | msf: Msf, | |
| 28 | ||
| 29 | pub fn openFile(self: *Pdb, allocator: *mem.Allocator, file_name: []u8) !void { | |
| 30 | self.in_file = try os.File.openRead(file_name[0..]); | |
| 31 | self.allocator = allocator; | |
| 32 | ||
| 33 | try self.msf.openFile(allocator, &self.in_file); | |
| 34 | } | |
| 35 | ||
| 36 | pub fn getStream(self: *Pdb, stream: StreamType) ?*MsfStream { | |
| 37 | const id = @enumToInt(stream); | |
| 38 | if (id < self.msf.streams.len) | |
| 39 | return &self.msf.streams.items[id]; | |
| 40 | return null; | |
| 41 | } | |
| 42 | ||
| 43 | pub fn getSourceLine(self: *Pdb, address: usize) !void { | |
| 44 | const dbi = self.getStream(StreamType.Dbi) orelse return error.CorruptedFile; | |
| 45 | ||
| 46 | // Dbi Header | |
| 47 | try dbi.seekForward(@sizeOf(u32) * 3 + @sizeOf(u16) * 6); | |
| 48 | warn("dbi stream at {} (file offset)\n", dbi.getFilePos()); | |
| 49 | const module_info_size = try dbi.stream.readIntLe(u32); | |
| 50 | const section_contribution_size = try dbi.stream.readIntLe(u32); | |
| 51 | const section_map_size = try dbi.stream.readIntLe(u32); | |
| 52 | const source_info_size = try dbi.stream.readIntLe(u32); | |
| 53 | warn("module_info_size: {}\n", module_info_size); | |
| 54 | warn("section_contribution_size: {}\n", section_contribution_size); | |
| 55 | warn("section_map_size: {}\n", section_map_size); | |
| 56 | warn("source_info_size: {}\n", source_info_size); | |
| 57 | try dbi.seekForward(@sizeOf(u32) * 5 + @sizeOf(u16) * 2); | |
| 58 | warn("after header dbi stream at {} (file offset)\n", dbi.getFilePos()); | |
| 59 | ||
| 60 | // Module Info Substream | |
| 61 | try dbi.seekForward(@sizeOf(u32) + @sizeOf(u16) + @sizeOf(u8) * 2); | |
| 62 | const offset = try dbi.stream.readIntLe(u32); | |
| 63 | const size = try dbi.stream.readIntLe(u32); | |
| 64 | try dbi.seekForward(@sizeOf(u32)); | |
| 65 | const module_index = try dbi.stream.readIntLe(u16); | |
| 66 | warn("module {} of size {} at {}\n", module_index, size, offset); | |
| 67 | ||
| 68 | // TODO: locate corresponding source line information | |
| 69 | } | |
| 70 | }; | |
| 71 | ||
| 72 | // see https://llvm.org/docs/PDB/MsfFile.html | |
| 73 | const Msf = struct { | |
| 74 | superblock: SuperBlock, | |
| 75 | directory: MsfStream, | |
| 76 | streams: ArrayList(MsfStream), | |
| 77 | ||
| 78 | fn openFile(self: *Msf, allocator: *mem.Allocator, file: *os.File) !void { | |
| 79 | var file_stream = io.FileInStream.init(file); | |
| 80 | const in = &file_stream.stream; | |
| 81 | ||
| 82 | var magic: SuperBlock.FileMagicBuffer = undefined; | |
| 83 | try in.readNoEof(magic[0..]); | |
| 84 | warn("magic: '{}'\n", magic); | |
| 85 | ||
| 86 | if (!mem.eql(u8, magic, SuperBlock.FileMagic)) | |
| 87 | return error.InvalidPdbMagic; | |
| 88 | ||
| 89 | self.superblock = SuperBlock { | |
| 90 | .block_size = try in.readIntLe(u32), | |
| 91 | .free_block_map_block = try in.readIntLe(u32), | |
| 92 | .num_blocks = try in.readIntLe(u32), | |
| 93 | .num_directory_bytes = try in.readIntLe(u32), | |
| 94 | .unknown = try in.readIntLe(u32), | |
| 95 | .block_map_addr = try in.readIntLe(u32), | |
| 96 | }; | |
| 97 | ||
| 98 | switch (self.superblock.block_size) { | |
| 99 | 512, 1024, 2048, 4096 => {}, // llvm only uses 4096 | |
| 100 | else => return error.InvalidPdbMagic | |
| 101 | } | |
| 102 | ||
| 103 | if (self.superblock.fileSize() != try file.getEndPos()) | |
| 104 | return error.CorruptedFile; // Should always stand. | |
| 105 | ||
| 106 | self.directory = try MsfStream.init( | |
| 107 | self.superblock.block_size, | |
| 108 | self.superblock.blocksOccupiedByDirectoryStream(), | |
| 109 | self.superblock.blockMapAddr(), | |
| 110 | file, | |
| 111 | allocator | |
| 112 | ); | |
| 113 | ||
| 114 | const stream_count = try self.directory.stream.readIntLe(u32); | |
| 115 | warn("stream count {}\n", stream_count); | |
| 116 | ||
| 117 | var stream_sizes = ArrayList(u32).init(allocator); | |
| 118 | try stream_sizes.resize(stream_count); | |
| 119 | for (stream_sizes.toSlice()) |*s| { | |
| 120 | const size = try self.directory.stream.readIntLe(u32); | |
| 121 | s.* = blockCountFromSize(size, self.superblock.block_size); | |
| 122 | warn("stream {}B {} blocks\n", size, s.*); | |
| 123 | } | |
| 124 | ||
| 125 | self.streams = ArrayList(MsfStream).init(allocator); | |
| 126 | try self.streams.resize(stream_count); | |
| 127 | for (self.streams.toSlice()) |*ss, i| { | |
| 128 | ss.* = try MsfStream.init( | |
| 129 | self.superblock.block_size, | |
| 130 | stream_sizes.items[i], | |
| 131 | try file.getPos(), // We're reading the jagged array of block indices when creating streams so the file is always at the right position. | |
| 132 | file, | |
| 133 | allocator | |
| 134 | ); | |
| 135 | } | |
| 136 | } | |
| 137 | }; | |
| 138 | ||
| 139 | fn blockCountFromSize(size: u32, block_size: u32) u32 { | |
| 140 | return (size + block_size - 1) / block_size; | |
| 141 | } | |
| 142 | ||
| 143 | const SuperBlock = struct { | |
| 144 | const FileMagic = "Microsoft C/C++ MSF 7.00\r\n" ++ []u8 { 0x1A, 'D', 'S', 0, 0, 0}; | |
| 145 | const FileMagicBuffer = @typeOf(FileMagic); | |
| 146 | ||
| 147 | block_size: u32, | |
| 148 | free_block_map_block: u32, | |
| 149 | num_blocks: u32, | |
| 150 | num_directory_bytes: u32, | |
| 151 | unknown: u32, | |
| 152 | block_map_addr: u32, | |
| 153 | ||
| 154 | fn fileSize(self: *const SuperBlock) usize { | |
| 155 | return self.num_blocks * self.block_size; | |
| 156 | } | |
| 157 | ||
| 158 | fn blockMapAddr(self: *const SuperBlock) usize { | |
| 159 | return self.block_size * self.block_map_addr; | |
| 160 | } | |
| 161 | ||
| 162 | fn blocksOccupiedByDirectoryStream(self: *const SuperBlock) u32 { | |
| 163 | return blockCountFromSize(self.num_directory_bytes, self.block_size); | |
| 164 | } | |
| 165 | }; | |
| 166 | ||
| 167 | const MsfStream = struct { | |
| 168 | in_file: *os.File, | |
| 169 | pos: usize, | |
| 170 | blocks: ArrayList(u32), | |
| 171 | block_size: u32, | |
| 172 | ||
| 173 | fn init(block_size: u32, block_count: u32, pos: usize, file: *os.File, allocator: *mem.Allocator) !MsfStream { | |
| 174 | var stream = MsfStream { | |
| 175 | .in_file = file, | |
| 176 | .pos = 0, | |
| 177 | .blocks = ArrayList(u32).init(allocator), | |
| 178 | .block_size = block_size, | |
| 179 | .stream = Stream { | |
| 180 | .readFn = readFn, | |
| 181 | }, | |
| 182 | }; | |
| 183 | ||
| 184 | try stream.blocks.resize(block_count); | |
| 185 | ||
| 186 | var file_stream = io.FileInStream.init(file); | |
| 187 | const in = &file_stream.stream; | |
| 188 | try file.seekTo(pos); | |
| 189 | ||
| 190 | warn("stream with blocks"); | |
| 191 | var i: u32 = 0; | |
| 192 | while (i < block_count) : (i += 1) { | |
| 193 | stream.blocks.items[i] = try in.readIntLe(u32); | |
| 194 | warn(" {}", stream.blocks.items[i]); | |
| 195 | } | |
| 196 | warn("\n"); | |
| 197 | ||
| 198 | return stream; | |
| 199 | } | |
| 200 | ||
| 201 | fn read(self: *MsfStream, buffer: []u8) !usize { | |
| 202 | var block_id = self.pos / self.block_size; | |
| 203 | var block = self.blocks.items[block_id]; | |
| 204 | var offset = self.pos % self.block_size; | |
| 205 | ||
| 206 | try self.in_file.seekTo(block * self.block_size + offset); | |
| 207 | var file_stream = io.FileInStream.init(self.in_file); | |
| 208 | const in = &file_stream.stream; | |
| 209 | ||
| 210 | var size: usize = 0; | |
| 211 | for (buffer) |*byte| { | |
| 212 | byte.* = try in.readByte(); | |
| 213 | ||
| 214 | offset += 1; | |
| 215 | size += 1; | |
| 216 | ||
| 217 | // If we're at the end of a block, go to the next one. | |
| 218 | if (offset == self.block_size) | |
| 219 | { | |
| 220 | offset = 0; | |
| 221 | block_id += 1; | |
| 222 | block = self.blocks.items[block_id]; | |
| 223 | try self.in_file.seekTo(block * self.block_size); | |
| 224 | } | |
| 225 | } | |
| 226 | ||
| 227 | self.pos += size; | |
| 228 | return size; | |
| 229 | } | |
| 230 | ||
| 231 | fn seekForward(self: *MsfStream, len: usize) !void { | |
| 232 | self.pos += len; | |
| 233 | if (self.pos >= self.blocks.len * self.block_size) | |
| 234 | return error.EOF; | |
| 235 | } | |
| 236 | ||
| 237 | fn seekTo(self: *MsfStream, len: usize) !void { | |
| 238 | self.pos = len; | |
| 239 | if (self.pos >= self.blocks.len * self.block_size) | |
| 240 | return error.EOF; | |
| 241 | } | |
| 242 | ||
| 243 | fn getSize(self: *const MsfStream) usize { | |
| 244 | return self.blocks.len * self.block_size; | |
| 245 | } | |
| 246 | ||
| 247 | fn getFilePos(self: *const MsfStream) usize { | |
| 248 | const block_id = self.pos / self.block_size; | |
| 249 | const block = self.blocks.items[block_id]; | |
| 250 | const offset = self.pos % self.block_size; | |
| 251 | ||
| 252 | return block * self.block_size + offset; | |
| 253 | } | |
| 254 | ||
| 255 | /// Implementation of InStream trait for Pdb.MsfStream | |
| 256 | pub const Error = @typeOf(read).ReturnType.ErrorSet; | |
| 257 | pub const Stream = io.InStream(Error); | |
| 258 | ||
| 259 | stream: Stream, | |
| 260 | ||
| 261 | fn readFn(in_stream: *Stream, buffer: []u8) Error!usize { | |
| 262 | const self = @fieldParentPtr(MsfStream, "stream", in_stream); | |
| 263 | return self.read(buffer); | |
| 264 | } | |
| 265 | }; |