blob: 6ae19f10182f4abd4716fb5c7440f7030bcaf91c [file] [log] [blame]
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "syzygy/pdb/pdb_reader.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "syzygy/pdb/pdb_file_stream.h"
namespace pdb {
namespace {
bool GetFileSize(FILE* file, uint32* size) {
DCHECK(file != NULL);
DCHECK(size != NULL);
if (fseek(file, 0, SEEK_END) != 0) {
LOG(ERROR) << "Failed seeking to end of file.";
return false;
}
long temp = ftell(file);
if (temp == -1L) {
LOG(ERROR) << "Failed to read stream position.";
return false;
}
DCHECK_GT(temp, 0);
(*size) = static_cast<uint32>(temp);
return true;
}
uint32 GetNumPages(const PdbHeader& header, uint32 num_bytes) {
return (num_bytes + header.page_size - 1) / header.page_size;
}
} // namespace
bool PdbReader::Read(const base::FilePath& pdb_path, PdbFile* pdb_file) {
DCHECK(pdb_file != NULL);
pdb_file->Clear();
scoped_refptr<RefCountedFILE> file(new RefCountedFILE(
base::OpenFile(pdb_path, "rb")));
if (!file->file()) {
LOG(ERROR) << "Unable to open '" << pdb_path.value() << "'.";
return false;
}
// Get the file size.
uint32 file_size = 0;
if (!GetFileSize(file->file(), &file_size)) {
LOG(ERROR) << "Unable to determine size of '" << pdb_path.value() << "'.";
return false;
}
PdbHeader header = { 0 };
// Read the header from the first page in the file. The page size we use here
// is irrelevant as after reading the header we get the actual page size in
// use by the PDB and from then on use that.
uint32 header_page = 0;
scoped_refptr<PdbFileStream> header_stream(new PdbFileStream(
file, sizeof(header), &header_page, kPdbPageSize));
if (!header_stream->Read(&header, 1)) {
LOG(ERROR) << "Failed to read PDB file header.";
return false;
}
// Sanity checks.
if (header.num_pages * header.page_size != file_size) {
LOG(ERROR) << "Invalid PDB file size.";
return false;
}
if (memcmp(header.magic_string, kPdbHeaderMagicString,
sizeof(kPdbHeaderMagicString)) != 0) {
LOG(ERROR) << "Invalid PDB magic string.";
return false;
}
// Load the directory page list (a sequence of uint32 page numbers that is
// itself written across multiple root pages). To do this we need to know how
// many pages are required to represent the directory, then we load a stream
// containing that many page pointers from the root pages array.
int num_dir_pages = static_cast<int>(GetNumPages(header,
header.directory_size));
scoped_refptr<PdbFileStream> dir_page_stream(new PdbFileStream(
file, num_dir_pages * sizeof(uint32),
header.root_pages, header.page_size));
scoped_ptr<uint32[]> dir_pages(new uint32[num_dir_pages]);
if (dir_pages.get() == NULL) {
LOG(ERROR) << "Failed to allocate directory pages.";
return false;
}
if (!dir_page_stream->Read(dir_pages.get(), num_dir_pages)) {
LOG(ERROR) << "Failed to read directory page stream.";
return false;
}
// Load the actual directory.
int dir_size = static_cast<int>(header.directory_size / sizeof(uint32));
scoped_refptr<PdbFileStream> dir_stream(new PdbFileStream(
file, header.directory_size, dir_pages.get(), header.page_size));
std::vector<uint32> directory(dir_size);
if (!dir_stream->Read(&directory[0], dir_size)) {
LOG(ERROR) << "Failed to read directory stream.";
return false;
}
// Iterate through the streams and construct PdbStreams.
const uint32& num_streams = directory[0];
const uint32* stream_lengths = &(directory[1]);
const uint32* stream_pages = &(directory[1 + num_streams]);
uint32 page_index = 0;
for (uint32 stream_index = 0; stream_index < num_streams; ++stream_index) {
pdb_file->AppendStream(new PdbFileStream(file,
stream_lengths[stream_index],
stream_pages + page_index,
header.page_size));
page_index += GetNumPages(header, stream_lengths[stream_index]);
}
return true;
}
} // namespace pdb