blob: e45b0b700941722bdc6f6da975dc8c95a2ac084e [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_symbol_record.h"
#include <string>
#include "base/strings/stringprintf.h"
#include "syzygy/common/align.h"
#include "syzygy/pdb/pdb_reader.h"
#include "syzygy/pdb/pdb_util.h"
#include "third_party/cci/Files/CvInfo.h"
namespace cci = Microsoft_Cci_Pdb;
namespace pdb {
bool ReadSymbolRecord(PdbStream* stream,
size_t symbol_table_size,
SymbolRecordVector* symbol_vector) {
DCHECK(stream != NULL);
DCHECK(symbol_vector != NULL);
size_t stream_end = stream->pos() + symbol_table_size;
if (stream_end > stream->length()) {
LOG(ERROR) << "The specified symbol table size exceeds the size of the "
<< "stream.";
return false;
}
// Process each symbol present in the stream. For now we only save their
// starting positions, their lengths and their types to be able to dump them.
while (stream->pos() < stream_end) {
uint16 len = 0;
uint16 symbol_type = 0;
if (!stream->Read(&len, 1)) {
LOG(ERROR) << "Unable to read a symbol record length.";
return false;
}
size_t symbol_start = stream->pos();
if (!stream->Read(&symbol_type, 1)) {
LOG(ERROR) << "Unable to read a symbol record type.";
return false;
}
SymbolRecord sym_record;
sym_record.type = symbol_type;
sym_record.start_position = stream->pos();
sym_record.len = len - sizeof(symbol_type);
symbol_vector->push_back(sym_record);
if (stream->pos() != stream_end && !stream->Seek(symbol_start + len)) {
LOG(ERROR) << "Unable to seek to the end of the symbol record.";
return false;
}
}
return true;
}
// Reads symbols from the given symbol stream until the end of the stream.
bool VisitSymbols(VisitSymbolsCallback callback,
size_t symbol_table_size,
bool has_header,
PdbStream* symbols) {
DCHECK(symbols != NULL);
size_t symbol_table_end = symbols->pos() + symbol_table_size;
if (symbol_table_end > symbols->length()) {
LOG(ERROR) << "Symbol table size provided exceeds stream length.";
return false;
}
if (has_header) {
uint32 stream_type = 0;
if (!symbols->Read(&stream_type, 1)) {
LOG(ERROR) << "Unable to read symbol stream type.";
return false;
}
if (stream_type != cci::C13) {
LOG(ERROR) << "Unexpected symbol stream type (" << stream_type
<< ").";
return false;
}
}
// Read the symbols from the linker symbol stream. We try to read at least
// one symbol without checking the stream position.
while (symbols->pos() < symbol_table_end) {
uint16 symbol_length = 0;
if (!symbols->Read(&symbol_length, 1)) {
LOG(ERROR) << "Unable to read symbol length from symbol stream.";
return false;
}
// We can see empty symbols in the symbol stream.
if (symbol_length == 0) {
// TODO(chrisha): I've only seen these as terminators thus far. Validate
// this fact for all symbol streams. If we find this to be true, we
// can break here and double check that we've consumed the entire
// stream content.
continue;
}
if (symbol_length < 2) {
LOG(ERROR) << "Symbol length too short to hold symbol type.";
return false;
}
// Remember the position in the stream where the next symbol lies. This is
// to be used for seeking later.
size_t symbol_end = symbols->pos() + symbol_length;
uint16 symbol_type = 0;
if (!symbols->Read(&symbol_type, 1)) {
LOG(ERROR) << "Failed to read symbol type from symbol stream.";
return false;
}
if (symbol_end > symbol_table_end) {
LOG(ERROR) << "Encountered symbol length that exceeds table size.";
return false;
}
// We provide the length of the symbol data to the callback, exclusive of
// the symbol type header.
if (!callback.Run(symbol_length - 2, symbol_type, symbols))
return false;
if (!symbols->Seek(symbol_end)) {
LOG(ERROR) << "Failed to seek past symbol in symbol stream.";
return false;
}
}
return true;
}
} // namespace pdb