| ... | ... | @@ -76,7 +76,7 @@ pub const Pdb = struct { |
| 76 | 76 | msf: Msf, |
| 77 | 77 | |
| 78 | 78 | pub fn openFile(self: *Pdb, allocator: *mem.Allocator, file_name: []u8) !void { |
| 79 | | self.in_file = try os.File.openRead(file_name[0..]); |
| 79 | self.in_file = try os.File.openRead(file_name); |
| 80 | 80 | self.allocator = allocator; |
| 81 | 81 | |
| 82 | 82 | try self.msf.openFile(allocator, self.in_file); |
| ... | ... | @@ -85,7 +85,7 @@ pub const Pdb = struct { |
| 85 | 85 | pub fn getStream(self: *Pdb, stream: StreamType) ?*MsfStream { |
| 86 | 86 | const id = @enumToInt(stream); |
| 87 | 87 | if (id < self.msf.streams.len) |
| 88 | | return &self.msf.streams.items[id]; |
| 88 | return &self.msf.streams[id]; |
| 89 | 89 | return null; |
| 90 | 90 | } |
| 91 | 91 | |
| ... | ... | @@ -101,17 +101,31 @@ pub const Pdb = struct { |
| 101 | 101 | // Module Info Substream |
| 102 | 102 | var mod_info_offset: usize = 0; |
| 103 | 103 | while (mod_info_offset < header.ModInfoSize) { |
| 104 | const march_forward_bytes = dbi.getFilePos() % 4; |
| 105 | if (march_forward_bytes != 0) { |
| 106 | try dbi.seekForward(march_forward_bytes); |
| 107 | mod_info_offset += march_forward_bytes; |
| 108 | } |
| 104 | 109 | var mod_info: ModInfo = undefined; |
| 105 | 110 | try dbi.stream.readStruct(ModInfo, &mod_info); |
| 106 | 111 | std.debug.warn("{}\n", mod_info); |
| 107 | 112 | mod_info_offset += @sizeOf(ModInfo); |
| 108 | 113 | |
| 109 | 114 | const module_name = try dbi.readNullTermString(self.allocator); |
| 110 | | std.debug.warn("module_name {}\n", module_name); |
| 115 | std.debug.warn("module_name '{}'\n", module_name); |
| 111 | 116 | mod_info_offset += module_name.len + 1; |
| 112 | 117 | |
| 118 | //if (mem.eql(u8, module_name, "piler_rt.obj")) { |
| 119 | // std.debug.warn("detected bad thing\n"); |
| 120 | // try dbi.seekTo(dbi.pos - |
| 121 | // "c:\\msys64\\home\\andy\\zig\\build-llvm6-msvc-release\\.\\zig-cache\\compiler_rt.obj\x00".len - |
| 122 | // @sizeOf(ModInfo)); |
| 123 | // mod_info_offset -= module_name.len + 1; |
| 124 | // continue; |
| 125 | //} |
| 126 | |
| 113 | 127 | const obj_file_name = try dbi.readNullTermString(self.allocator); |
| 114 | | std.debug.warn("obj_file_name {}\n", obj_file_name); |
| 128 | std.debug.warn("obj_file_name '{}'\n", obj_file_name); |
| 115 | 129 | mod_info_offset += obj_file_name.len + 1; |
| 116 | 130 | } |
| 117 | 131 | std.debug.warn("end modules\n"); |
| ... | ... | @@ -123,66 +137,55 @@ pub const Pdb = struct { |
| 123 | 137 | |
| 124 | 138 | // see https://llvm.org/docs/PDB/MsfFile.html |
| 125 | 139 | const Msf = struct { |
| 126 | | superblock: SuperBlock, |
| 127 | 140 | directory: MsfStream, |
| 128 | | streams: ArrayList(MsfStream), |
| 141 | streams: []MsfStream, |
| 129 | 142 | |
| 130 | 143 | fn openFile(self: *Msf, allocator: *mem.Allocator, file: os.File) !void { |
| 131 | 144 | var file_stream = io.FileInStream.init(file); |
| 132 | 145 | const in = &file_stream.stream; |
| 133 | 146 | |
| 134 | | var magic: SuperBlock.FileMagicBuffer = undefined; |
| 135 | | try in.readNoEof(magic[0..]); |
| 136 | | warn("magic: '{}'\n", magic); |
| 137 | | |
| 138 | | if (!mem.eql(u8, magic, SuperBlock.FileMagic)) |
| 139 | | return error.InvalidDebugInfo; |
| 147 | var superblock: SuperBlock = undefined; |
| 148 | try in.readStruct(SuperBlock, &superblock); |
| 140 | 149 | |
| 141 | | self.superblock = SuperBlock { |
| 142 | | .block_size = try in.readIntLe(u32), |
| 143 | | .free_block_map_block = try in.readIntLe(u32), |
| 144 | | .num_blocks = try in.readIntLe(u32), |
| 145 | | .num_directory_bytes = try in.readIntLe(u32), |
| 146 | | .unknown = try in.readIntLe(u32), |
| 147 | | .block_map_addr = try in.readIntLe(u32), |
| 148 | | }; |
| 150 | if (!mem.eql(u8, superblock.FileMagic, SuperBlock.file_magic)) |
| 151 | return error.InvalidDebugInfo; |
| 149 | 152 | |
| 150 | | switch (self.superblock.block_size) { |
| 151 | | 512, 1024, 2048, 4096 => {}, // llvm only uses 4096 |
| 153 | switch (superblock.BlockSize) { |
| 154 | // llvm only supports 4096 but we can handle any of these values |
| 155 | 512, 1024, 2048, 4096 => {}, |
| 152 | 156 | else => return error.InvalidDebugInfo |
| 153 | 157 | } |
| 154 | 158 | |
| 155 | | if (self.superblock.fileSize() != try file.getEndPos()) |
| 156 | | return error.InvalidDebugInfo; // Should always stand. |
| 159 | if (superblock.NumBlocks * superblock.BlockSize != try file.getEndPos()) |
| 160 | return error.InvalidDebugInfo; |
| 157 | 161 | |
| 158 | 162 | self.directory = try MsfStream.init( |
| 159 | | self.superblock.block_size, |
| 160 | | self.superblock.blocksOccupiedByDirectoryStream(), |
| 161 | | self.superblock.blockMapAddr(), |
| 163 | superblock.BlockSize, |
| 164 | blockCountFromSize(superblock.NumDirectoryBytes, superblock.BlockSize), |
| 165 | superblock.BlockSize * superblock.BlockMapAddr, |
| 162 | 166 | file, |
| 163 | | allocator |
| 167 | allocator, |
| 164 | 168 | ); |
| 165 | 169 | |
| 166 | 170 | const stream_count = try self.directory.stream.readIntLe(u32); |
| 167 | 171 | warn("stream count {}\n", stream_count); |
| 168 | 172 | |
| 169 | | var stream_sizes = ArrayList(u32).init(allocator); |
| 170 | | try stream_sizes.resize(stream_count); |
| 171 | | for (stream_sizes.toSlice()) |*s| { |
| 173 | const stream_sizes = try allocator.alloc(u32, stream_count); |
| 174 | for (stream_sizes) |*s| { |
| 172 | 175 | const size = try self.directory.stream.readIntLe(u32); |
| 173 | | s.* = blockCountFromSize(size, self.superblock.block_size); |
| 176 | s.* = blockCountFromSize(size, superblock.BlockSize); |
| 174 | 177 | warn("stream {}B {} blocks\n", size, s.*); |
| 175 | 178 | } |
| 176 | 179 | |
| 177 | | self.streams = ArrayList(MsfStream).init(allocator); |
| 178 | | try self.streams.resize(stream_count); |
| 179 | | for (self.streams.toSlice()) |*ss, i| { |
| 180 | | ss.* = try MsfStream.init( |
| 181 | | self.superblock.block_size, |
| 182 | | stream_sizes.items[i], |
| 183 | | try file.getPos(), // We're reading the jagged array of block indices when creating streams so the file is always at the right position. |
| 180 | self.streams = try allocator.alloc(MsfStream, stream_count); |
| 181 | for (self.streams) |*stream, i| { |
| 182 | stream.* = try MsfStream.init( |
| 183 | superblock.BlockSize, |
| 184 | stream_sizes[i], |
| 185 | // MsfStream.init expects the file to be at the part where it reads [N]u32 |
| 186 | try file.getPos(), |
| 184 | 187 | file, |
| 185 | | allocator |
| 188 | allocator, |
| 186 | 189 | ); |
| 187 | 190 | } |
| 188 | 191 | } |
| ... | ... | @@ -192,34 +195,53 @@ fn blockCountFromSize(size: u32, block_size: u32) u32 { |
| 192 | 195 | return (size + block_size - 1) / block_size; |
| 193 | 196 | } |
| 194 | 197 | |
| 195 | | const SuperBlock = struct { |
| 196 | | const FileMagic = "Microsoft C/C++ MSF 7.00\r\n" ++ []u8 { 0x1A, 'D', 'S', 0, 0, 0}; |
| 197 | | const FileMagicBuffer = @typeOf(FileMagic); |
| 198 | | |
| 199 | | block_size: u32, |
| 200 | | free_block_map_block: u32, |
| 201 | | num_blocks: u32, |
| 202 | | num_directory_bytes: u32, |
| 203 | | unknown: u32, |
| 204 | | block_map_addr: u32, |
| 205 | | |
| 206 | | fn fileSize(self: *const SuperBlock) usize { |
| 207 | | return self.num_blocks * self.block_size; |
| 208 | | } |
| 209 | | |
| 210 | | fn blockMapAddr(self: *const SuperBlock) usize { |
| 211 | | return self.block_size * self.block_map_addr; |
| 212 | | } |
| 198 | // https://llvm.org/docs/PDB/MsfFile.html#the-superblock |
| 199 | const SuperBlock = packed struct { |
| 200 | /// The LLVM docs list a space between C / C++ but empirically this is not the case. |
| 201 | const file_magic = "Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53\x00\x00\x00"; |
| 202 | |
| 203 | FileMagic: [file_magic.len]u8, |
| 204 | |
| 205 | /// The block size of the internal file system. Valid values are 512, 1024, |
| 206 | /// 2048, and 4096 bytes. Certain aspects of the MSF file layout vary depending |
| 207 | /// on the block sizes. For the purposes of LLVM, we handle only block sizes of |
| 208 | /// 4KiB, and all further discussion assumes a block size of 4KiB. |
| 209 | BlockSize: u32, |
| 210 | |
| 211 | /// The index of a block within the file, at which begins a bitfield representing |
| 212 | /// the set of all blocks within the file which are “free” (i.e. the data within |
| 213 | /// that block is not used). See The Free Block Map for more information. Important: |
| 214 | /// FreeBlockMapBlock can only be 1 or 2! |
| 215 | FreeBlockMapBlock: u32, |
| 216 | |
| 217 | /// The total number of blocks in the file. NumBlocks * BlockSize should equal the |
| 218 | /// size of the file on disk. |
| 219 | NumBlocks: u32, |
| 220 | |
| 221 | /// The size of the stream directory, in bytes. The stream directory contains |
| 222 | /// information about each stream’s size and the set of blocks that it occupies. |
| 223 | /// It will be described in more detail later. |
| 224 | NumDirectoryBytes: u32, |
| 225 | |
| 226 | Unknown: u32, |
| 227 | |
| 228 | /// The index of a block within the MSF file. At this block is an array of |
| 229 | /// ulittle32_t’s listing the blocks that the stream directory resides on. |
| 230 | /// For large MSF files, the stream directory (which describes the block |
| 231 | /// layout of each stream) may not fit entirely on a single block. As a |
| 232 | /// result, this extra layer of indirection is introduced, whereby this |
| 233 | /// block contains the list of blocks that the stream directory occupies, |
| 234 | /// and the stream directory itself can be stitched together accordingly. |
| 235 | /// The number of ulittle32_t’s in this array is given by |
| 236 | /// ceil(NumDirectoryBytes / BlockSize). |
| 237 | BlockMapAddr: u32, |
| 213 | 238 | |
| 214 | | fn blocksOccupiedByDirectoryStream(self: *const SuperBlock) u32 { |
| 215 | | return blockCountFromSize(self.num_directory_bytes, self.block_size); |
| 216 | | } |
| 217 | 239 | }; |
| 218 | 240 | |
| 219 | 241 | const MsfStream = struct { |
| 220 | 242 | in_file: os.File, |
| 221 | 243 | pos: usize, |
| 222 | | blocks: ArrayList(u32), |
| 244 | blocks: []u32, |
| 223 | 245 | block_size: u32, |
| 224 | 246 | |
| 225 | 247 | /// Implementation of InStream trait for Pdb.MsfStream |
| ... | ... | @@ -232,15 +254,13 @@ const MsfStream = struct { |
| 232 | 254 | var stream = MsfStream { |
| 233 | 255 | .in_file = file, |
| 234 | 256 | .pos = 0, |
| 235 | | .blocks = ArrayList(u32).init(allocator), |
| 257 | .blocks = try allocator.alloc(u32, block_count), |
| 236 | 258 | .block_size = block_size, |
| 237 | 259 | .stream = Stream { |
| 238 | 260 | .readFn = readFn, |
| 239 | 261 | }, |
| 240 | 262 | }; |
| 241 | 263 | |
| 242 | | try stream.blocks.resize(block_count); |
| 243 | | |
| 244 | 264 | var file_stream = io.FileInStream.init(file); |
| 245 | 265 | const in = &file_stream.stream; |
| 246 | 266 | try file.seekTo(pos); |
| ... | ... | @@ -248,8 +268,8 @@ const MsfStream = struct { |
| 248 | 268 | warn("stream with blocks"); |
| 249 | 269 | var i: u32 = 0; |
| 250 | 270 | while (i < block_count) : (i += 1) { |
| 251 | | stream.blocks.items[i] = try in.readIntLe(u32); |
| 252 | | warn(" {}", stream.blocks.items[i]); |
| 271 | stream.blocks[i] = try in.readIntLe(u32); |
| 272 | warn(" {}", stream.blocks[i]); |
| 253 | 273 | } |
| 254 | 274 | warn("\n"); |
| 255 | 275 | |
| ... | ... | @@ -270,9 +290,13 @@ const MsfStream = struct { |
| 270 | 290 | |
| 271 | 291 | fn read(self: *MsfStream, buffer: []u8) !usize { |
| 272 | 292 | var block_id = self.pos / self.block_size; |
| 273 | | var block = self.blocks.items[block_id]; |
| 293 | var block = self.blocks[block_id]; |
| 274 | 294 | var offset = self.pos % self.block_size; |
| 275 | 295 | |
| 296 | //std.debug.warn("seek {} read {}B: block_id={} block={} offset={}\n", |
| 297 | // block * self.block_size + offset, |
| 298 | // buffer.len, block_id, block, offset); |
| 299 | |
| 276 | 300 | try self.in_file.seekTo(block * self.block_size + offset); |
| 277 | 301 | var file_stream = io.FileInStream.init(self.in_file); |
| 278 | 302 | const in = &file_stream.stream; |
| ... | ... | @@ -285,11 +309,10 @@ const MsfStream = struct { |
| 285 | 309 | size += 1; |
| 286 | 310 | |
| 287 | 311 | // If we're at the end of a block, go to the next one. |
| 288 | | if (offset == self.block_size) |
| 289 | | { |
| 312 | if (offset == self.block_size) { |
| 290 | 313 | offset = 0; |
| 291 | 314 | block_id += 1; |
| 292 | | block = self.blocks.items[block_id]; |
| 315 | block = self.blocks[block_id]; |
| 293 | 316 | try self.in_file.seekTo(block * self.block_size); |
| 294 | 317 | } |
| 295 | 318 | } |
| ... | ... | @@ -314,9 +337,9 @@ const MsfStream = struct { |
| 314 | 337 | return self.blocks.len * self.block_size; |
| 315 | 338 | } |
| 316 | 339 | |
| 317 | | fn getFilePos(self: *const MsfStream) usize { |
| 340 | fn getFilePos(self: MsfStream) usize { |
| 318 | 341 | const block_id = self.pos / self.block_size; |
| 319 | | const block = self.blocks.items[block_id]; |
| 342 | const block = self.blocks[block_id]; |
| 320 | 343 | const offset = self.pos % self.block_size; |
| 321 | 344 | |
| 322 | 345 | return block * self.block_size + offset; |