| author | |
| committer | |
| log | 2ec9a11646c792a046b4601e0b99f8e182416a6c |
| tree | f7fa33a35d73ce246ca4423508e35c8012946293 |
| parent | f47655eb6d42590a89f177e7354c998a8d88582d |
Currently does:
- read COFF executable file
- locate and load corresponding .pdb file
- expose .pdb content as streams (PDB format)7 files changed, 563 insertions(+), 3 deletions(-)
CMakeLists.txt+2| ... | @@ -427,6 +427,7 @@ set(ZIG_STD_FILES | ... | @@ -427,6 +427,7 @@ set(ZIG_STD_FILES |
| 427 | "c/index.zig" | 427 | "c/index.zig" |
| 428 | "c/linux.zig" | 428 | "c/linux.zig" |
| 429 | "c/windows.zig" | 429 | "c/windows.zig" |
| 430 | "coff.zig" | ||
| 430 | "crypto/blake2.zig" | 431 | "crypto/blake2.zig" |
| 431 | "crypto/hmac.zig" | 432 | "crypto/hmac.zig" |
| 432 | "crypto/index.zig" | 433 | "crypto/index.zig" |
| ... | @@ -544,6 +545,7 @@ set(ZIG_STD_FILES | ... | @@ -544,6 +545,7 @@ set(ZIG_STD_FILES |
| 544 | "os/windows/index.zig" | 545 | "os/windows/index.zig" |
| 545 | "os/windows/util.zig" | 546 | "os/windows/util.zig" |
| 546 | "os/zen.zig" | 547 | "os/zen.zig" |
| 548 | "pdb.zig" | ||
| 547 | "rand/index.zig" | 549 | "rand/index.zig" |
| 548 | "rand/ziggurat.zig" | 550 | "rand/ziggurat.zig" |
| 549 | "segmented_list.zig" | 551 | "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+45-2| ... | @@ -6,6 +6,9 @@ const os = std.os; | ... | @@ -6,6 +6,9 @@ const os = std.os; |
| 6 | const elf = std.elf; | 6 | const elf = std.elf; |
| 7 | const DW = std.dwarf; | 7 | const DW = std.dwarf; |
| 8 | const macho = std.macho; | 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 | ||
| ... | @@ -197,7 +200,13 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us | ... | @@ -197,7 +200,13 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us |
| 197 | const ptr_hex = "0x{x}"; | 200 | const ptr_hex = "0x{x}"; |
| 198 | 201 | ||
| 199 | switch (builtin.os) { | 202 | switch (builtin.os) { |
| 200 | builtin.Os.windows => return error.UnsupportedDebugInfo, | 203 | builtin.Os.windows => { |
| 204 | const base_address = @ptrToInt(windows.GetModuleHandleA(null)); // returned HMODULE points to our executable file in memory | ||
| 205 | const relative_address = address - base_address; | ||
| 206 | std.debug.warn("{x} - {x} => {x}\n", address, base_address, relative_address); | ||
| 207 | try debug_info.pdb.getSourceLine(relative_address); | ||
| 208 | return error.UnsupportedDebugInfo; | ||
| 209 | }, | ||
| 201 | builtin.Os.macosx => { | 210 | builtin.Os.macosx => { |
| 202 | // TODO(bnoordhuis) It's theoretically possible to obtain the | 211 | // TODO(bnoordhuis) It's theoretically possible to obtain the |
| 203 | // compilation unit from the symbtab but it's not that useful | 212 | // compilation unit from the symbtab but it's not that useful |
| ... | @@ -288,7 +297,38 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace { | ... | @@ -288,7 +297,38 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace { |
| 288 | return st; | 297 | return st; |
| 289 | }, | 298 | }, |
| 290 | builtin.ObjectFormat.coff => { | 299 | builtin.ObjectFormat.coff => { |
| 291 | return error.TodoSupportCoffDebugInfo; | 300 | var coff_file: coff.Coff = undefined; |
| 301 | coff_file.in_file = try os.openSelfExe(); | ||
| 302 | coff_file.allocator = allocator; | ||
| 303 | defer coff_file.in_file.close(); | ||
| 304 | |||
| 305 | try coff_file.loadHeader(); | ||
| 306 | |||
| 307 | var path: [windows.MAX_PATH]u8 = undefined; | ||
| 308 | const len = try coff_file.getPdbPath(path[0..]); | ||
| 309 | std.debug.warn("pdb path {}\n", path[0..len]); | ||
| 310 | |||
| 311 | const st = try allocator.create(ElfStackTrace); | ||
| 312 | errdefer allocator.destroy(st); | ||
| 313 | st.* = ElfStackTrace { | ||
| 314 | .pdb = undefined, | ||
| 315 | }; | ||
| 316 | |||
| 317 | try st.pdb.openFile(allocator, path[0..len]); | ||
| 318 | |||
| 319 | var pdb_stream = st.pdb.getStream(pdb.StreamType.Pdb) orelse return error.CorruptedFile; | ||
| 320 | std.debug.warn("pdb real filepos {}\n", pdb_stream.getFilePos()); | ||
| 321 | const version = try pdb_stream.stream.readIntLe(u32); | ||
| 322 | const signature = try pdb_stream.stream.readIntLe(u32); | ||
| 323 | const age = try pdb_stream.stream.readIntLe(u32); | ||
| 324 | var guid: [16]u8 = undefined; | ||
| 325 | try pdb_stream.stream.readNoEof(guid[0..]); | ||
| 326 | if (!mem.eql(u8, coff_file.guid, guid) or coff_file.age != age) | ||
| 327 | return error.CorruptedFile; | ||
| 328 | std.debug.warn("v {} s {} a {}\n", version, signature, age); | ||
| 329 | // We validated the executable and pdb match. | ||
| 330 | |||
| 331 | return st; | ||
| 292 | }, | 332 | }, |
| 293 | builtin.ObjectFormat.wasm => { | 333 | builtin.ObjectFormat.wasm => { |
| 294 | return error.TodoSupportCOFFDebugInfo; | 334 | return error.TodoSupportCOFFDebugInfo; |
| ... | @@ -339,6 +379,9 @@ pub const ElfStackTrace = switch (builtin.os) { | ... | @@ -339,6 +379,9 @@ pub const ElfStackTrace = switch (builtin.os) { |
| 339 | self.symbol_table.deinit(); | 379 | self.symbol_table.deinit(); |
| 340 | } | 380 | } |
| 341 | }, | 381 | }, |
| 382 | builtin.Os.windows => struct { | ||
| 383 | pdb: pdb.Pdb, | ||
| 384 | }, | ||
| 342 | else => struct { | 385 | else => struct { |
| 343 | self_exe_file: os.File, | 386 | self_exe_file: os.File, |
| 344 | elf: elf.Elf, | 387 | elf: elf.Elf, |
std/index.zig+4| ... | @@ -13,6 +13,7 @@ pub const atomic = @import("atomic/index.zig"); | ... | @@ -13,6 +13,7 @@ pub const atomic = @import("atomic/index.zig"); |
| 13 | pub const base64 = @import("base64.zig"); | 13 | pub const base64 = @import("base64.zig"); |
| 14 | pub const build = @import("build.zig"); | 14 | pub const build = @import("build.zig"); |
| 15 | pub const c = @import("c/index.zig"); | 15 | pub const c = @import("c/index.zig"); |
| 16 | pub const coff = @import("coff.zig"); | ||
| 16 | pub const crypto = @import("crypto/index.zig"); | 17 | pub const crypto = @import("crypto/index.zig"); |
| 17 | pub const cstr = @import("cstr.zig"); | 18 | pub const cstr = @import("cstr.zig"); |
| 18 | pub const debug = @import("debug/index.zig"); | 19 | pub const debug = @import("debug/index.zig"); |
| ... | @@ -30,6 +31,7 @@ pub const math = @import("math/index.zig"); | ... | @@ -30,6 +31,7 @@ pub const math = @import("math/index.zig"); |
| 30 | pub const mem = @import("mem.zig"); | 31 | pub const mem = @import("mem.zig"); |
| 31 | pub const net = @import("net.zig"); | 32 | pub const net = @import("net.zig"); |
| 32 | pub const os = @import("os/index.zig"); | 33 | pub const os = @import("os/index.zig"); |
| 34 | pub const pdb = @import("pdb.zig"); | ||
| 33 | pub const rand = @import("rand/index.zig"); | 35 | pub const rand = @import("rand/index.zig"); |
| 34 | pub const sort = @import("sort.zig"); | 36 | pub const sort = @import("sort.zig"); |
| 35 | pub const unicode = @import("unicode.zig"); | 37 | pub const unicode = @import("unicode.zig"); |
| ... | @@ -49,6 +51,7 @@ test "std" { | ... | @@ -49,6 +51,7 @@ test "std" { |
| 49 | _ = @import("base64.zig"); | 51 | _ = @import("base64.zig"); |
| 50 | _ = @import("build.zig"); | 52 | _ = @import("build.zig"); |
| 51 | _ = @import("c/index.zig"); | 53 | _ = @import("c/index.zig"); |
| 54 | _ = @import("coff.zig"); | ||
| 52 | _ = @import("crypto/index.zig"); | 55 | _ = @import("crypto/index.zig"); |
| 53 | _ = @import("cstr.zig"); | 56 | _ = @import("cstr.zig"); |
| 54 | _ = @import("debug/index.zig"); | 57 | _ = @import("debug/index.zig"); |
| ... | @@ -67,6 +70,7 @@ test "std" { | ... | @@ -67,6 +70,7 @@ test "std" { |
| 67 | _ = @import("heap.zig"); | 70 | _ = @import("heap.zig"); |
| 68 | _ = @import("os/index.zig"); | 71 | _ = @import("os/index.zig"); |
| 69 | _ = @import("rand/index.zig"); | 72 | _ = @import("rand/index.zig"); |
| 73 | _ = @import("pdb.zig"); | ||
| 70 | _ = @import("sort.zig"); | 74 | _ = @import("sort.zig"); |
| 71 | _ = @import("unicode.zig"); | 75 | _ = @import("unicode.zig"); |
| 72 | _ = @import("zig/index.zig"); | 76 | _ = @import("zig/index.zig"); |
std/os/index.zig+7-1| ... | @@ -1896,13 +1896,19 @@ pub fn openSelfExe() !os.File { | ... | @@ -1896,13 +1896,19 @@ pub fn openSelfExe() !os.File { |
| 1896 | const self_exe_path = try selfExePath(&fixed_allocator.allocator); | 1896 | const self_exe_path = try selfExePath(&fixed_allocator.allocator); |
| 1897 | return os.File.openRead(&fixed_allocator.allocator, self_exe_path); | 1897 | return os.File.openRead(&fixed_allocator.allocator, self_exe_path); |
| 1898 | }, | 1898 | }, |
| 1899 | Os.windows => { | ||
| 1900 | var fixed_buffer_mem: [windows.MAX_PATH * 2]u8 = undefined; | ||
| 1901 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | ||
| 1902 | const self_exe_path = try selfExePath(&fixed_allocator.allocator); | ||
| 1903 | return os.File.openRead(&fixed_allocator.allocator, self_exe_path); | ||
| 1904 | }, | ||
| 1899 | else => @compileError("Unsupported OS"), | 1905 | else => @compileError("Unsupported OS"), |
| 1900 | } | 1906 | } |
| 1901 | } | 1907 | } |
| 1902 | 1908 | ||
| 1903 | test "openSelfExe" { | 1909 | test "openSelfExe" { |
| 1904 | switch (builtin.os) { | 1910 | switch (builtin.os) { |
| 1905 | Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(), | 1911 | Os.linux, Os.macosx, Os.ios, Os.windows => (try openSelfExe()).close(), |
| 1906 | else => return, // Unsupported OS. | 1912 | else => return, // Unsupported OS. |
| 1907 | } | 1913 | } |
| 1908 | } | 1914 | } |
std/os/windows/index.zig+2| ... | @@ -105,6 +105,8 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( | ... | @@ -105,6 +105,8 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( |
| 105 | dwFlags: DWORD, | 105 | dwFlags: DWORD, |
| 106 | ) DWORD; | 106 | ) DWORD; |
| 107 | 107 | ||
| 108 | pub extern "kernel32" stdcallcc fn GetModuleHandleA(lpModuleName: ?LPCSTR) HMODULE; | ||
| 109 | |||
| 108 | pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; | 110 | pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; |
| 109 | 111 | ||
| 110 | pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void; | 112 | pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void; |
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(allocator, 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 = u16(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 | }; | ||
| \ No newline at end of file | |||