| 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,6 +444,7 @@ set(ZIG_STD_FILES |
| 444 | "c/index.zig" | 444 | "c/index.zig" |
| 445 | "c/linux.zig" | 445 | "c/linux.zig" |
| 446 | "c/windows.zig" | 446 | "c/windows.zig" |
| 447 | "coff.zig" | ||
| 447 | "crypto/blake2.zig" | 448 | "crypto/blake2.zig" |
| 448 | "crypto/hmac.zig" | 449 | "crypto/hmac.zig" |
| 449 | "crypto/index.zig" | 450 | "crypto/index.zig" |
| ... | @@ -583,6 +584,7 @@ set(ZIG_STD_FILES | ... | @@ -583,6 +584,7 @@ set(ZIG_STD_FILES |
| 583 | "os/windows/user32.zig" | 584 | "os/windows/user32.zig" |
| 584 | "os/windows/util.zig" | 585 | "os/windows/util.zig" |
| 585 | "os/zen.zig" | 586 | "os/zen.zig" |
| 587 | "pdb.zig" | ||
| 586 | "rand/index.zig" | 588 | "rand/index.zig" |
| 587 | "rand/ziggurat.zig" | 589 | "rand/ziggurat.zig" |
| 588 | "segmented_list.zig" | 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,8 +4,11 @@ const mem = std.mem; |
| 4 | const io = std.io; | 4 | const io = std.io; |
| 5 | const os = std.os; | 5 | const os = std.os; |
| 6 | const elf = std.elf; | 6 | const elf = std.elf; |
| 7 | const macho = std.macho; | ||
| 8 | const DW = std.dwarf; | 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 | const ArrayList = std.ArrayList; | 12 | const ArrayList = std.ArrayList; |
| 10 | const builtin = @import("builtin"); | 13 | const builtin = @import("builtin"); |
| 11 | 14 | ||
| ... | @@ -228,14 +231,19 @@ pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: us | ... | @@ -228,14 +231,19 @@ pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: us |
| 228 | switch (builtin.os) { | 231 | switch (builtin.os) { |
| 229 | builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color), | 232 | builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color), |
| 230 | builtin.Os.linux => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color), | 233 | builtin.Os.linux => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color), |
| 231 | builtin.Os.windows => { | 234 | builtin.Os.windows => return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color), |
| 232 | // TODO https://github.com/ziglang/zig/issues/721 | ||
| 233 | return error.UnsupportedOperatingSystem; | ||
| 234 | }, | ||
| 235 | else => return error.UnsupportedOperatingSystem, | 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 | fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol { | 247 | fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol { |
| 240 | var min: usize = 0; | 248 | var min: usize = 0; |
| 241 | var max: usize = symbols.len - 1; // Exclude sentinel. | 249 | var max: usize = symbols.len - 1; // Exclude sentinel. |
| ... | @@ -372,14 +380,44 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { | ... | @@ -372,14 +380,44 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { |
| 372 | switch (builtin.os) { | 380 | switch (builtin.os) { |
| 373 | builtin.Os.linux => return openSelfDebugInfoLinux(allocator), | 381 | builtin.Os.linux => return openSelfDebugInfoLinux(allocator), |
| 374 | builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator), | 382 | builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator), |
| 375 | builtin.Os.windows => { | 383 | builtin.Os.windows => return openSelfDebugInfoWindows(allocator), |
| 376 | // TODO: https://github.com/ziglang/zig/issues/721 | ||
| 377 | return error.UnsupportedOperatingSystem; | ||
| 378 | }, | ||
| 379 | else => return error.UnsupportedOperatingSystem, | 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 | fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo { | 421 | fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo { |
| 384 | var di = DebugInfo{ | 422 | var di = DebugInfo{ |
| 385 | .self_exe_file = undefined, | 423 | .self_exe_file = undefined, |
| ... | @@ -578,7 +616,10 @@ pub const DebugInfo = switch (builtin.os) { | ... | @@ -578,7 +616,10 @@ pub const DebugInfo = switch (builtin.os) { |
| 578 | return self.ofiles.allocator; | 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 | self_exe_file: os.File, | 623 | self_exe_file: os.File, |
| 583 | elf: elf.Elf, | 624 | elf: elf.Elf, |
| 584 | debug_info: *elf.SectionHeader, | 625 | debug_info: *elf.SectionHeader, |
| ... | @@ -604,6 +645,7 @@ pub const DebugInfo = switch (builtin.os) { | ... | @@ -604,6 +645,7 @@ pub const DebugInfo = switch (builtin.os) { |
| 604 | self.elf.close(); | 645 | self.elf.close(); |
| 605 | } | 646 | } |
| 606 | }, | 647 | }, |
| 648 | else => @compileError("Unsupported OS"), | ||
| 607 | }; | 649 | }; |
| 608 | 650 | ||
| 609 | const PcRange = struct { | 651 | const PcRange = struct { |
std/index.zig+4| ... | @@ -15,6 +15,7 @@ pub const atomic = @import("atomic/index.zig"); | ... | @@ -15,6 +15,7 @@ pub const atomic = @import("atomic/index.zig"); |
| 15 | pub const base64 = @import("base64.zig"); | 15 | pub const base64 = @import("base64.zig"); |
| 16 | pub const build = @import("build.zig"); | 16 | pub const build = @import("build.zig"); |
| 17 | pub const c = @import("c/index.zig"); | 17 | pub const c = @import("c/index.zig"); |
| 18 | pub const coff = @import("coff.zig"); | ||
| 18 | pub const crypto = @import("crypto/index.zig"); | 19 | pub const crypto = @import("crypto/index.zig"); |
| 19 | pub const cstr = @import("cstr.zig"); | 20 | pub const cstr = @import("cstr.zig"); |
| 20 | pub const debug = @import("debug/index.zig"); | 21 | pub const debug = @import("debug/index.zig"); |
| ... | @@ -33,6 +34,7 @@ pub const math = @import("math/index.zig"); | ... | @@ -33,6 +34,7 @@ pub const math = @import("math/index.zig"); |
| 33 | pub const mem = @import("mem.zig"); | 34 | pub const mem = @import("mem.zig"); |
| 34 | pub const net = @import("net.zig"); | 35 | pub const net = @import("net.zig"); |
| 35 | pub const os = @import("os/index.zig"); | 36 | pub const os = @import("os/index.zig"); |
| 37 | pub const pdb = @import("pdb.zig"); | ||
| 36 | pub const rand = @import("rand/index.zig"); | 38 | pub const rand = @import("rand/index.zig"); |
| 37 | pub const rb = @import("rb.zig"); | 39 | pub const rb = @import("rb.zig"); |
| 38 | pub const sort = @import("sort.zig"); | 40 | pub const sort = @import("sort.zig"); |
| ... | @@ -56,6 +58,7 @@ test "std" { | ... | @@ -56,6 +58,7 @@ test "std" { |
| 56 | _ = @import("base64.zig"); | 58 | _ = @import("base64.zig"); |
| 57 | _ = @import("build.zig"); | 59 | _ = @import("build.zig"); |
| 58 | _ = @import("c/index.zig"); | 60 | _ = @import("c/index.zig"); |
| 61 | _ = @import("coff.zig"); | ||
| 59 | _ = @import("crypto/index.zig"); | 62 | _ = @import("crypto/index.zig"); |
| 60 | _ = @import("cstr.zig"); | 63 | _ = @import("cstr.zig"); |
| 61 | _ = @import("debug/index.zig"); | 64 | _ = @import("debug/index.zig"); |
| ... | @@ -74,6 +77,7 @@ test "std" { | ... | @@ -74,6 +77,7 @@ test "std" { |
| 74 | _ = @import("heap.zig"); | 77 | _ = @import("heap.zig"); |
| 75 | _ = @import("os/index.zig"); | 78 | _ = @import("os/index.zig"); |
| 76 | _ = @import("rand/index.zig"); | 79 | _ = @import("rand/index.zig"); |
| 80 | _ = @import("pdb.zig"); | ||
| 77 | _ = @import("sort.zig"); | 81 | _ = @import("sort.zig"); |
| 78 | _ = @import("unicode.zig"); | 82 | _ = @import("unicode.zig"); |
| 79 | _ = @import("zig/index.zig"); | 83 | _ = @import("zig/index.zig"); |
std/os/file.zig+39-24| ... | @@ -48,18 +48,23 @@ pub const File = struct { | ... | @@ -48,18 +48,23 @@ pub const File = struct { |
| 48 | return openReadC(&path_c); | 48 | return openReadC(&path_c); |
| 49 | } | 49 | } |
| 50 | if (is_windows) { | 50 | if (is_windows) { |
| 51 | const handle = try os.windowsOpen( | 51 | const path_w = try windows_util.sliceToPrefixedFileW(path); |
| 52 | path, | 52 | return openReadW(&path_w); |
| 53 | windows.GENERIC_READ, | ||
| 54 | windows.FILE_SHARE_READ, | ||
| 55 | windows.OPEN_EXISTING, | ||
| 56 | windows.FILE_ATTRIBUTE_NORMAL, | ||
| 57 | ); | ||
| 58 | return openHandle(handle); | ||
| 59 | } | 53 | } |
| 60 | @compileError("Unsupported OS"); | 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 | /// Calls `openWriteMode` with os.File.default_mode for the mode. | 68 | /// Calls `openWriteMode` with os.File.default_mode for the mode. |
| 64 | pub fn openWrite(path: []const u8) OpenError!File { | 69 | pub fn openWrite(path: []const u8) OpenError!File { |
| 65 | return openWriteMode(path, os.File.default_mode); | 70 | return openWriteMode(path, os.File.default_mode); |
| ... | @@ -74,19 +79,24 @@ pub const File = struct { | ... | @@ -74,19 +79,24 @@ pub const File = struct { |
| 74 | const fd = try os.posixOpen(path, flags, file_mode); | 79 | const fd = try os.posixOpen(path, flags, file_mode); |
| 75 | return openHandle(fd); | 80 | return openHandle(fd); |
| 76 | } else if (is_windows) { | 81 | } else if (is_windows) { |
| 77 | const handle = try os.windowsOpen( | 82 | const path_w = try windows_util.sliceToPrefixedFileW(path); |
| 78 | path, | 83 | return openWriteModeW(&path_w, file_mode); |
| 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); | ||
| 85 | } else { | 84 | } else { |
| 86 | @compileError("TODO implement openWriteMode for this OS"); | 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 | /// If the path does not exist it will be created. | 100 | /// If the path does not exist it will be created. |
| 91 | /// If a file already exists in the destination this returns OpenError.PathAlreadyExists | 101 | /// If a file already exists in the destination this returns OpenError.PathAlreadyExists |
| 92 | /// Call close to clean up. | 102 | /// Call close to clean up. |
| ... | @@ -96,19 +106,24 @@ pub const File = struct { | ... | @@ -96,19 +106,24 @@ pub const File = struct { |
| 96 | const fd = try os.posixOpen(path, flags, file_mode); | 106 | const fd = try os.posixOpen(path, flags, file_mode); |
| 97 | return openHandle(fd); | 107 | return openHandle(fd); |
| 98 | } else if (is_windows) { | 108 | } else if (is_windows) { |
| 99 | const handle = try os.windowsOpen( | 109 | const path_w = try windows_util.sliceToPrefixedFileW(path); |
| 100 | path, | 110 | return openWriteNoClobberW(&path_w, file_mode); |
| 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); | ||
| 107 | } else { | 111 | } else { |
| 108 | @compileError("TODO implement openWriteMode for this OS"); | 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 | pub fn openHandle(handle: os.FileHandle) File { | 127 | pub fn openHandle(handle: os.FileHandle) File { |
| 113 | return File{ .handle = handle }; | 128 | return File{ .handle = handle }; |
| 114 | } | 129 | } |
std/os/index.zig+22-13| ... | @@ -57,6 +57,7 @@ pub const windowsWaitSingle = windows_util.windowsWaitSingle; | ... | @@ -57,6 +57,7 @@ pub const windowsWaitSingle = windows_util.windowsWaitSingle; |
| 57 | pub const windowsWrite = windows_util.windowsWrite; | 57 | pub const windowsWrite = windows_util.windowsWrite; |
| 58 | pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; | 58 | pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; |
| 59 | pub const windowsOpen = windows_util.windowsOpen; | 59 | pub const windowsOpen = windows_util.windowsOpen; |
| 60 | pub const windowsOpenW = windows_util.windowsOpenW; | ||
| 60 | pub const windowsLoadDll = windows_util.windowsLoadDll; | 61 | pub const windowsLoadDll = windows_util.windowsLoadDll; |
| 61 | pub const windowsUnloadDll = windows_util.windowsUnloadDll; | 62 | pub const windowsUnloadDll = windows_util.windowsUnloadDll; |
| 62 | pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; | 63 | pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; |
| ... | @@ -2103,15 +2104,33 @@ pub fn openSelfExe() !os.File { | ... | @@ -2103,15 +2104,33 @@ pub fn openSelfExe() !os.File { |
| 2103 | buf[self_exe_path.len] = 0; | 2104 | buf[self_exe_path.len] = 0; |
| 2104 | return os.File.openReadC(self_exe_path.ptr); | 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 | else => @compileError("Unsupported OS"), | 2112 | else => @compileError("Unsupported OS"), |
| 2107 | } | 2113 | } |
| 2108 | } | 2114 | } |
| 2109 | 2115 | ||
| 2110 | test "openSelfExe" { | 2116 | test "openSelfExe" { |
| 2111 | switch (builtin.os) { | 2117 | switch (builtin.os) { |
| 2112 | Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(), | 2118 | Os.linux, Os.macosx, Os.ios, Os.windows => (try openSelfExe()).close(), |
| 2113 | else => return error.SkipZigTest, // Unsupported OS | 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 | /// Get the path to the current executable. | 2136 | /// Get the path to the current executable. |
| ... | @@ -2128,17 +2147,7 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { | ... | @@ -2128,17 +2147,7 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 2128 | switch (builtin.os) { | 2147 | switch (builtin.os) { |
| 2129 | Os.linux => return readLink(out_buffer, "/proc/self/exe"), | 2148 | Os.linux => return readLink(out_buffer, "/proc/self/exe"), |
| 2130 | Os.windows => { | 2149 | Os.windows => { |
| 2131 | var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | 2150 | const utf16le_slice = try selfExePathW(&utf16le_buf); |
| 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]; | ||
| 2142 | // Trust that Windows gives us valid UTF-16LE. | 2151 | // Trust that Windows gives us valid UTF-16LE. |
| 2143 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; | 2152 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; |
| 2144 | return out_buffer[0..end_index]; | 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,6 +92,8 @@ pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR |
| 92 | pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD; | 92 | pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD; |
| 93 | pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD; | 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 | pub extern "kernel32" stdcallcc fn GetLastError() DWORD; | 97 | pub extern "kernel32" stdcallcc fn GetLastError() DWORD; |
| 96 | 98 | ||
| 97 | pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( | 99 | pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( |
std/os/windows/util.zig+14-5| ... | @@ -118,16 +118,14 @@ pub const OpenError = error{ | ... | @@ -118,16 +118,14 @@ pub const OpenError = error{ |
| 118 | Unexpected, | 118 | Unexpected, |
| 119 | }; | 119 | }; |
| 120 | 120 | ||
| 121 | pub fn windowsOpen( | 121 | pub fn windowsOpenW( |
| 122 | file_path: []const u8, | 122 | file_path_w: [*]const u16, |
| 123 | desired_access: windows.DWORD, | 123 | desired_access: windows.DWORD, |
| 124 | share_mode: windows.DWORD, | 124 | share_mode: windows.DWORD, |
| 125 | creation_disposition: windows.DWORD, | 125 | creation_disposition: windows.DWORD, |
| 126 | flags_and_attrs: windows.DWORD, | 126 | flags_and_attrs: windows.DWORD, |
| 127 | ) OpenError!windows.HANDLE { | 127 | ) OpenError!windows.HANDLE { |
| 128 | const file_path_w = try sliceToPrefixedFileW(file_path); | 128 | const result = windows.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); |
| 129 | |||
| 130 | const result = windows.CreateFileW(&file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); | ||
| 131 | 129 | ||
| 132 | if (result == windows.INVALID_HANDLE_VALUE) { | 130 | if (result == windows.INVALID_HANDLE_VALUE) { |
| 133 | const err = windows.GetLastError(); | 131 | const err = windows.GetLastError(); |
| ... | @@ -146,6 +144,17 @@ pub fn windowsOpen( | ... | @@ -146,6 +144,17 @@ pub fn windowsOpen( |
| 146 | return result; | 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 | /// Caller must free result. | 158 | /// Caller must free result. |
| 150 | pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u8 { | 159 | pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u8 { |
| 151 | // count bytes needed | 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 | }; | ||