blob: cbc1ee4f01aaed4feec84b6134d5a4eee42e1897 [file] [log] [blame]
// Copyright 2013 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/pe/coff_file.h"
#include "gtest/gtest.h"
#include "syzygy/core/unittest_util.h"
#include "syzygy/pe/unittest_util.h"
namespace pe {
namespace {
using core::AbsoluteAddress;
using core::FileOffsetAddress;
using core::RelativeAddress;
class CoffFileTest : public testing::PELibUnitTest {
typedef testing::PELibUnitTest Super;
public:
CoffFileTest() {
}
virtual void SetUp() override {
Super::SetUp();
test_dll_obj_path_ =
testing::GetExeTestDataRelativePath(testing::kTestDllCoffObjName);
}
protected:
base::FilePath test_dll_obj_path_;
CoffFile image_file_;
DISALLOW_COPY_AND_ASSIGN(CoffFileTest);
};
const char kTestExportPrefix[] = "?TestExport@@";
const char kTestStaticPrefix[] = "?TestStatic@@";
} // namespace
TEST_F(CoffFileTest, Init) {
EXPECT_TRUE(image_file_.file_header() == NULL);
EXPECT_TRUE(image_file_.section_headers() == NULL);
EXPECT_TRUE(image_file_.symbols() == NULL);
EXPECT_TRUE(image_file_.strings() == NULL);
ASSERT_TRUE(image_file_.Init(test_dll_obj_path_));
EXPECT_TRUE(image_file_.file_header() != NULL);
EXPECT_TRUE(image_file_.section_headers() != NULL);
EXPECT_TRUE(image_file_.symbols() != NULL);
EXPECT_TRUE(image_file_.strings() != NULL);
EXPECT_TRUE(image_file_.file_header()->PointerToSymbolTable != 0);
// Technically, SizeOfOptionalHeader could be non-zero, but it is
// deprecated and MSVC should not generate such a header.
EXPECT_EQ(0u, image_file_.file_header()->SizeOfOptionalHeader);
}
TEST_F(CoffFileTest, TranslateSectionOffsets) {
ASSERT_TRUE(image_file_.Init(test_dll_obj_path_));
size_t num_sections = image_file_.file_header()->NumberOfSections;
for (size_t i = 0; i < num_sections; ++i) {
const IMAGE_SECTION_HEADER* header = image_file_.section_header(i);
if (!image_file_.IsSectionMapped(i))
continue;
size_t offset = header->SizeOfRawData / 2;
FileOffsetAddress addr(FileOffsetAddress::kInvalidAddress);
ASSERT_TRUE(image_file_.SectionOffsetToFileOffset(i, offset, &addr));
EXPECT_NE(addr, FileOffsetAddress::kInvalidAddress);
size_t new_section_index = kInvalidSection;
size_t new_offset = SIZE_MAX;
ASSERT_TRUE(image_file_.FileOffsetToSectionOffset(
addr, &new_section_index, &new_offset));
EXPECT_EQ(i, new_section_index);
EXPECT_EQ(offset, new_offset);
}
}
TEST_F(CoffFileTest, GetSymbols) {
ASSERT_TRUE(image_file_.Init(test_dll_obj_path_));
size_t num_symbols = image_file_.file_header()->NumberOfSymbols;
EXPECT_GT(num_symbols, 0u);
for (size_t i = 0; i < num_symbols; ++i) {
EXPECT_TRUE(image_file_.symbol(i) != NULL);
}
}
TEST_F(CoffFileTest, DecodeRelocs) {
ASSERT_TRUE(image_file_.Init(test_dll_obj_path_));
CoffFile::RelocMap reloc_map;
image_file_.DecodeRelocs(&reloc_map);
EXPECT_FALSE(reloc_map.empty());
// Validate relocations.
CoffFile::RelocMap::const_iterator it = reloc_map.begin();
for (; it != reloc_map.end(); ++it) {
// Location to relocate must be mapped within the address space.
EXPECT_TRUE(image_file_.Contains(it->first, sizeof(void*)));
// Relocation must reference a valid symbol.
EXPECT_TRUE(image_file_.symbol(it->second->SymbolTableIndex) != NULL);
}
}
TEST_F(CoffFileTest, GetSymbolName) {
ASSERT_TRUE(image_file_.Init(test_dll_obj_path_));
size_t num_symbols = image_file_.file_header()->NumberOfSymbols;
const IMAGE_SYMBOL* symbol = NULL;
for (size_t i = 0; i < num_symbols; i += 1 + symbol->NumberOfAuxSymbols) {
symbol = image_file_.symbol(i);
EXPECT_TRUE(image_file_.GetSymbolName(i) != NULL);
}
}
TEST_F(CoffFileTest, HaveTestSymbols) {
ASSERT_TRUE(image_file_.Init(test_dll_obj_path_));
bool have_dll_main = false;
bool have_test_export = false;
size_t num_symbols = image_file_.file_header()->NumberOfSymbols;
const IMAGE_SYMBOL* symbol = NULL;
for (size_t i = 0; i < num_symbols; i += 1 + symbol->NumberOfAuxSymbols) {
symbol = image_file_.symbol(i);
const char* symbol_name = image_file_.GetSymbolName(i);
if (strcmp(symbol_name, "_DllMain@12") == 0)
have_dll_main = true;
if (strncmp(symbol_name, kTestExportPrefix,
strlen(kTestExportPrefix)) == 0)
have_test_export = true;
}
EXPECT_TRUE(have_dll_main);
EXPECT_TRUE(have_test_export);
}
TEST_F(CoffFileTest, HaveStaticFunction) {
ASSERT_TRUE(image_file_.Init(test_dll_obj_path_));
const IMAGE_SYMBOL* test_static_symbol = NULL;
size_t num_symbols = image_file_.file_header()->NumberOfSymbols;
const IMAGE_SYMBOL* symbol = NULL;
for (size_t i = 0; i < num_symbols; i += 1 + symbol->NumberOfAuxSymbols) {
symbol = image_file_.symbol(i);
const char* symbol_name = image_file_.GetSymbolName(i);
if (strncmp(symbol_name, kTestStaticPrefix,
strlen(kTestStaticPrefix)) == 0) {
test_static_symbol = image_file_.symbol(i);
break;
}
}
ASSERT_TRUE(test_static_symbol != NULL);
ASSERT_EQ(0u, test_static_symbol->NumberOfAuxSymbols);
}
TEST_F(CoffFileTest, HaveFunctionAndLabels) {
ASSERT_TRUE(image_file_.Init(test_dll_obj_path_));
size_t num_functions = 0;
size_t num_labels = 0;
size_t num_symbols = image_file_.file_header()->NumberOfSymbols;
const IMAGE_SYMBOL* symbol = NULL;
for (size_t i = 0; i < num_symbols; i += 1 + symbol->NumberOfAuxSymbols) {
symbol = image_file_.symbol(i);
const IMAGE_SYMBOL* symbol = image_file_.symbol(i);
// Specifications say the DTYPE is in the MSB but it's really only
// shifted by 4, not 8.
num_functions += symbol->Type >> 4 == IMAGE_SYM_DTYPE_FUNCTION;
num_labels += symbol->StorageClass == IMAGE_SYM_CLASS_LABEL;
}
ASSERT_LT(0u, num_functions);
ASSERT_LT(0u, num_labels);
}
TEST(SimpleCoffFileTest, InitCodeView2Symbols) {
base::FilePath path = testing::GetSrcRelativePath(testing::kCodeView2Name);
CoffFile file;
EXPECT_TRUE(file.Init(path));
}
TEST(SimpleCoffFileTest, InitEmptyStringTable) {
base::FilePath path = testing::GetSrcRelativePath(
testing::kEmptyStringTableCoffName);
CoffFile file;
EXPECT_TRUE(file.Init(path));
}
} // namespace pe