2018-07-22 02:30:11 +08:00
|
|
|
const builtin = @import("builtin");
|
|
|
|
const std = @import("index.zig");
|
|
|
|
const io = std.io;
|
|
|
|
const math = std.math;
|
|
|
|
const mem = std.mem;
|
|
|
|
const os = std.os;
|
|
|
|
const warn = std.debug.warn;
|
|
|
|
|
|
|
|
const ArrayList = std.ArrayList;
|
|
|
|
|
|
|
|
pub const PdbError = error {
|
|
|
|
InvalidPdbMagic,
|
|
|
|
CorruptedFile,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub const StreamType = enum(u16) {
|
|
|
|
Pdb = 1,
|
|
|
|
Tpi = 2,
|
|
|
|
Dbi = 3,
|
|
|
|
Ipi = 4,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub const Pdb = struct {
|
|
|
|
in_file: os.File,
|
|
|
|
allocator: *mem.Allocator,
|
|
|
|
|
|
|
|
msf: Msf,
|
|
|
|
|
|
|
|
pub fn openFile(self: *Pdb, allocator: *mem.Allocator, file_name: []u8) !void {
|
2018-08-29 05:32:32 +08:00
|
|
|
self.in_file = try os.File.openRead(file_name[0..]);
|
2018-07-22 02:30:11 +08:00
|
|
|
self.allocator = allocator;
|
|
|
|
|
|
|
|
try self.msf.openFile(allocator, &self.in_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn getStream(self: *Pdb, stream: StreamType) ?*MsfStream {
|
2018-08-29 05:32:32 +08:00
|
|
|
const id = @enumToInt(stream);
|
2018-07-22 02:30:11 +08:00
|
|
|
if (id < self.msf.streams.len)
|
|
|
|
return &self.msf.streams.items[id];
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn getSourceLine(self: *Pdb, address: usize) !void {
|
|
|
|
const dbi = self.getStream(StreamType.Dbi) orelse return error.CorruptedFile;
|
|
|
|
|
|
|
|
// Dbi Header
|
|
|
|
try dbi.seekForward(@sizeOf(u32) * 3 + @sizeOf(u16) * 6);
|
|
|
|
warn("dbi stream at {} (file offset)\n", dbi.getFilePos());
|
|
|
|
const module_info_size = try dbi.stream.readIntLe(u32);
|
|
|
|
const section_contribution_size = try dbi.stream.readIntLe(u32);
|
|
|
|
const section_map_size = try dbi.stream.readIntLe(u32);
|
|
|
|
const source_info_size = try dbi.stream.readIntLe(u32);
|
|
|
|
warn("module_info_size: {}\n", module_info_size);
|
|
|
|
warn("section_contribution_size: {}\n", section_contribution_size);
|
|
|
|
warn("section_map_size: {}\n", section_map_size);
|
|
|
|
warn("source_info_size: {}\n", source_info_size);
|
|
|
|
try dbi.seekForward(@sizeOf(u32) * 5 + @sizeOf(u16) * 2);
|
|
|
|
warn("after header dbi stream at {} (file offset)\n", dbi.getFilePos());
|
|
|
|
|
|
|
|
// Module Info Substream
|
|
|
|
try dbi.seekForward(@sizeOf(u32) + @sizeOf(u16) + @sizeOf(u8) * 2);
|
|
|
|
const offset = try dbi.stream.readIntLe(u32);
|
|
|
|
const size = try dbi.stream.readIntLe(u32);
|
|
|
|
try dbi.seekForward(@sizeOf(u32));
|
|
|
|
const module_index = try dbi.stream.readIntLe(u16);
|
|
|
|
warn("module {} of size {} at {}\n", module_index, size, offset);
|
|
|
|
|
|
|
|
// TODO: locate corresponding source line information
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// see https://llvm.org/docs/PDB/MsfFile.html
|
|
|
|
const Msf = struct {
|
|
|
|
superblock: SuperBlock,
|
|
|
|
directory: MsfStream,
|
|
|
|
streams: ArrayList(MsfStream),
|
|
|
|
|
|
|
|
fn openFile(self: *Msf, allocator: *mem.Allocator, file: *os.File) !void {
|
|
|
|
var file_stream = io.FileInStream.init(file);
|
|
|
|
const in = &file_stream.stream;
|
|
|
|
|
|
|
|
var magic: SuperBlock.FileMagicBuffer = undefined;
|
|
|
|
try in.readNoEof(magic[0..]);
|
|
|
|
warn("magic: '{}'\n", magic);
|
|
|
|
|
|
|
|
if (!mem.eql(u8, magic, SuperBlock.FileMagic))
|
|
|
|
return error.InvalidPdbMagic;
|
|
|
|
|
|
|
|
self.superblock = SuperBlock {
|
|
|
|
.block_size = try in.readIntLe(u32),
|
|
|
|
.free_block_map_block = try in.readIntLe(u32),
|
|
|
|
.num_blocks = try in.readIntLe(u32),
|
|
|
|
.num_directory_bytes = try in.readIntLe(u32),
|
|
|
|
.unknown = try in.readIntLe(u32),
|
|
|
|
.block_map_addr = try in.readIntLe(u32),
|
|
|
|
};
|
|
|
|
|
|
|
|
switch (self.superblock.block_size) {
|
|
|
|
512, 1024, 2048, 4096 => {}, // llvm only uses 4096
|
|
|
|
else => return error.InvalidPdbMagic
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self.superblock.fileSize() != try file.getEndPos())
|
|
|
|
return error.CorruptedFile; // Should always stand.
|
|
|
|
|
|
|
|
self.directory = try MsfStream.init(
|
|
|
|
self.superblock.block_size,
|
|
|
|
self.superblock.blocksOccupiedByDirectoryStream(),
|
|
|
|
self.superblock.blockMapAddr(),
|
|
|
|
file,
|
|
|
|
allocator
|
|
|
|
);
|
|
|
|
|
|
|
|
const stream_count = try self.directory.stream.readIntLe(u32);
|
|
|
|
warn("stream count {}\n", stream_count);
|
|
|
|
|
|
|
|
var stream_sizes = ArrayList(u32).init(allocator);
|
|
|
|
try stream_sizes.resize(stream_count);
|
|
|
|
for (stream_sizes.toSlice()) |*s| {
|
|
|
|
const size = try self.directory.stream.readIntLe(u32);
|
|
|
|
s.* = blockCountFromSize(size, self.superblock.block_size);
|
|
|
|
warn("stream {}B {} blocks\n", size, s.*);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.streams = ArrayList(MsfStream).init(allocator);
|
|
|
|
try self.streams.resize(stream_count);
|
|
|
|
for (self.streams.toSlice()) |*ss, i| {
|
|
|
|
ss.* = try MsfStream.init(
|
|
|
|
self.superblock.block_size,
|
|
|
|
stream_sizes.items[i],
|
|
|
|
try file.getPos(), // We're reading the jagged array of block indices when creating streams so the file is always at the right position.
|
|
|
|
file,
|
|
|
|
allocator
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
fn blockCountFromSize(size: u32, block_size: u32) u32 {
|
|
|
|
return (size + block_size - 1) / block_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
const SuperBlock = struct {
|
|
|
|
const FileMagic = "Microsoft C/C++ MSF 7.00\r\n" ++ []u8 { 0x1A, 'D', 'S', 0, 0, 0};
|
|
|
|
const FileMagicBuffer = @typeOf(FileMagic);
|
|
|
|
|
|
|
|
block_size: u32,
|
|
|
|
free_block_map_block: u32,
|
|
|
|
num_blocks: u32,
|
|
|
|
num_directory_bytes: u32,
|
|
|
|
unknown: u32,
|
|
|
|
block_map_addr: u32,
|
|
|
|
|
|
|
|
fn fileSize(self: *const SuperBlock) usize {
|
|
|
|
return self.num_blocks * self.block_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn blockMapAddr(self: *const SuperBlock) usize {
|
|
|
|
return self.block_size * self.block_map_addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn blocksOccupiedByDirectoryStream(self: *const SuperBlock) u32 {
|
|
|
|
return blockCountFromSize(self.num_directory_bytes, self.block_size);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const MsfStream = struct {
|
|
|
|
in_file: *os.File,
|
|
|
|
pos: usize,
|
|
|
|
blocks: ArrayList(u32),
|
|
|
|
block_size: u32,
|
|
|
|
|
|
|
|
fn init(block_size: u32, block_count: u32, pos: usize, file: *os.File, allocator: *mem.Allocator) !MsfStream {
|
|
|
|
var stream = MsfStream {
|
|
|
|
.in_file = file,
|
|
|
|
.pos = 0,
|
|
|
|
.blocks = ArrayList(u32).init(allocator),
|
|
|
|
.block_size = block_size,
|
|
|
|
.stream = Stream {
|
|
|
|
.readFn = readFn,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
try stream.blocks.resize(block_count);
|
|
|
|
|
|
|
|
var file_stream = io.FileInStream.init(file);
|
|
|
|
const in = &file_stream.stream;
|
|
|
|
try file.seekTo(pos);
|
|
|
|
|
|
|
|
warn("stream with blocks");
|
|
|
|
var i: u32 = 0;
|
|
|
|
while (i < block_count) : (i += 1) {
|
|
|
|
stream.blocks.items[i] = try in.readIntLe(u32);
|
|
|
|
warn(" {}", stream.blocks.items[i]);
|
|
|
|
}
|
|
|
|
warn("\n");
|
|
|
|
|
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read(self: *MsfStream, buffer: []u8) !usize {
|
|
|
|
var block_id = self.pos / self.block_size;
|
|
|
|
var block = self.blocks.items[block_id];
|
|
|
|
var offset = self.pos % self.block_size;
|
|
|
|
|
|
|
|
try self.in_file.seekTo(block * self.block_size + offset);
|
|
|
|
var file_stream = io.FileInStream.init(self.in_file);
|
|
|
|
const in = &file_stream.stream;
|
|
|
|
|
|
|
|
var size: usize = 0;
|
|
|
|
for (buffer) |*byte| {
|
|
|
|
byte.* = try in.readByte();
|
|
|
|
|
|
|
|
offset += 1;
|
|
|
|
size += 1;
|
|
|
|
|
|
|
|
// If we're at the end of a block, go to the next one.
|
|
|
|
if (offset == self.block_size)
|
|
|
|
{
|
|
|
|
offset = 0;
|
|
|
|
block_id += 1;
|
|
|
|
block = self.blocks.items[block_id];
|
|
|
|
try self.in_file.seekTo(block * self.block_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.pos += size;
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn seekForward(self: *MsfStream, len: usize) !void {
|
|
|
|
self.pos += len;
|
|
|
|
if (self.pos >= self.blocks.len * self.block_size)
|
|
|
|
return error.EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn seekTo(self: *MsfStream, len: usize) !void {
|
|
|
|
self.pos = len;
|
|
|
|
if (self.pos >= self.blocks.len * self.block_size)
|
|
|
|
return error.EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn getSize(self: *const MsfStream) usize {
|
|
|
|
return self.blocks.len * self.block_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn getFilePos(self: *const MsfStream) usize {
|
|
|
|
const block_id = self.pos / self.block_size;
|
|
|
|
const block = self.blocks.items[block_id];
|
|
|
|
const offset = self.pos % self.block_size;
|
|
|
|
|
|
|
|
return block * self.block_size + offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Implementation of InStream trait for Pdb.MsfStream
|
|
|
|
pub const Error = @typeOf(read).ReturnType.ErrorSet;
|
|
|
|
pub const Stream = io.InStream(Error);
|
|
|
|
|
|
|
|
stream: Stream,
|
|
|
|
|
|
|
|
fn readFn(in_stream: *Stream, buffer: []u8) Error!usize {
|
|
|
|
const self = @fieldParentPtr(MsfStream, "stream", in_stream);
|
|
|
|
return self.read(buffer);
|
|
|
|
}
|
2018-08-29 05:32:32 +08:00
|
|
|
};
|