| 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 | 427 | "c/index.zig" |
| 428 | 428 | "c/linux.zig" |
| 429 | 429 | "c/windows.zig" |
| 430 | "coff.zig" | |
| 430 | 431 | "crypto/blake2.zig" |
| 431 | 432 | "crypto/hmac.zig" |
| 432 | 433 | "crypto/index.zig" |
| ... | ... | @@ -544,6 +545,7 @@ set(ZIG_STD_FILES |
| 544 | 545 | "os/windows/index.zig" |
| 545 | 546 | "os/windows/util.zig" |
| 546 | 547 | "os/zen.zig" |
| 548 | "pdb.zig" | |
| 547 | 549 | "rand/index.zig" |
| 548 | 550 | "rand/ziggurat.zig" |
| 549 | 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 | const elf = std.elf; |
| 7 | 7 | const DW = std.dwarf; |
| 8 | 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 | |
| ... | ... | @@ -197,7 +200,13 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us |
| 197 | 200 | const ptr_hex = "0x{x}"; |
| 198 | 201 | |
| 199 | 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 | 210 | builtin.Os.macosx => { |
| 202 | 211 | // TODO(bnoordhuis) It's theoretically possible to obtain the |
| 203 | 212 | // compilation unit from the symbtab but it's not that useful |
| ... | ... | @@ -288,7 +297,38 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace { |
| 288 | 297 | return st; |
| 289 | 298 | }, |
| 290 | 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 | 333 | builtin.ObjectFormat.wasm => { |
| 294 | 334 | return error.TodoSupportCOFFDebugInfo; |
| ... | ... | @@ -339,6 +379,9 @@ pub const ElfStackTrace = switch (builtin.os) { |
| 339 | 379 | self.symbol_table.deinit(); |
| 340 | 380 | } |
| 341 | 381 | }, |
| 382 | builtin.Os.windows => struct { | |
| 383 | pdb: pdb.Pdb, | |
| 384 | }, | |
| 342 | 385 | else => struct { |
| 343 | 386 | self_exe_file: os.File, |
| 344 | 387 | elf: elf.Elf, |
std/index.zig+4| ... | ... | @@ -13,6 +13,7 @@ pub const atomic = @import("atomic/index.zig"); |
| 13 | 13 | pub const base64 = @import("base64.zig"); |
| 14 | 14 | pub const build = @import("build.zig"); |
| 15 | 15 | pub const c = @import("c/index.zig"); |
| 16 | pub const coff = @import("coff.zig"); | |
| 16 | 17 | pub const crypto = @import("crypto/index.zig"); |
| 17 | 18 | pub const cstr = @import("cstr.zig"); |
| 18 | 19 | pub const debug = @import("debug/index.zig"); |
| ... | ... | @@ -30,6 +31,7 @@ pub const math = @import("math/index.zig"); |
| 30 | 31 | pub const mem = @import("mem.zig"); |
| 31 | 32 | pub const net = @import("net.zig"); |
| 32 | 33 | pub const os = @import("os/index.zig"); |
| 34 | pub const pdb = @import("pdb.zig"); | |
| 33 | 35 | pub const rand = @import("rand/index.zig"); |
| 34 | 36 | pub const sort = @import("sort.zig"); |
| 35 | 37 | pub const unicode = @import("unicode.zig"); |
| ... | ... | @@ -49,6 +51,7 @@ test "std" { |
| 49 | 51 | _ = @import("base64.zig"); |
| 50 | 52 | _ = @import("build.zig"); |
| 51 | 53 | _ = @import("c/index.zig"); |
| 54 | _ = @import("coff.zig"); | |
| 52 | 55 | _ = @import("crypto/index.zig"); |
| 53 | 56 | _ = @import("cstr.zig"); |
| 54 | 57 | _ = @import("debug/index.zig"); |
| ... | ... | @@ -67,6 +70,7 @@ test "std" { |
| 67 | 70 | _ = @import("heap.zig"); |
| 68 | 71 | _ = @import("os/index.zig"); |
| 69 | 72 | _ = @import("rand/index.zig"); |
| 73 | _ = @import("pdb.zig"); | |
| 70 | 74 | _ = @import("sort.zig"); |
| 71 | 75 | _ = @import("unicode.zig"); |
| 72 | 76 | _ = @import("zig/index.zig"); |
std/os/index.zig+7-1| ... | ... | @@ -1896,13 +1896,19 @@ pub fn openSelfExe() !os.File { |
| 1896 | 1896 | const self_exe_path = try selfExePath(&fixed_allocator.allocator); |
| 1897 | 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 | 1905 | else => @compileError("Unsupported OS"), |
| 1900 | 1906 | } |
| 1901 | 1907 | } |
| 1902 | 1908 | |
| 1903 | 1909 | test "openSelfExe" { |
| 1904 | 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 | 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 | 105 | dwFlags: DWORD, |
| 106 | 106 | ) DWORD; |
| 107 | 107 | |
| 108 | pub extern "kernel32" stdcallcc fn GetModuleHandleA(lpModuleName: ?LPCSTR) HMODULE; | |
| 109 | ||
| 108 | 110 | pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; |
| 109 | 111 | |
| 110 | 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 |