rel32_finder_unittest.cc 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // Copyright 2015 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <stddef.h>
  5. #include <stdint.h>
  6. #include <algorithm>
  7. #include <memory>
  8. #include <sstream>
  9. #include <string>
  10. #include "base/notreached.h"
  11. #include "courgette/base_test_unittest.h"
  12. #include "courgette/image_utils.h"
  13. #include "courgette/rel32_finder_x64.h"
  14. #include "courgette/rel32_finder_x86.h"
  15. #include "testing/gtest/include/gtest/gtest.h"
  16. namespace courgette {
  17. namespace {
  18. // Helper class to load and execute a Rel32Finder test case.
  19. class Rel32FinderTestCase {
  20. public:
  21. Rel32FinderTestCase(const std::string& test_data)
  22. : text_start_rva_(0),
  23. text_end_rva_(0),
  24. relocs_start_rva_(0),
  25. relocs_end_rva_(0),
  26. image_end_rva_(0) {
  27. LoadTestFromString(test_data);
  28. }
  29. void RunTestBasic(std::string name) {
  30. ASSERT_FALSE(text_data_.empty());
  31. finder_->Find(&text_data_[0], &text_data_[0] + text_data_.size(),
  32. text_start_rva_, text_end_rva_, abs32_locations_);
  33. std::vector<RVA> rel32_locations;
  34. finder_->SwapRel32Locations(&rel32_locations);
  35. EXPECT_EQ(expected_rel32_locations_, rel32_locations)
  36. << "From test case " << name << " (addresses are in hex)";
  37. }
  38. void CreateFinder(const std::string& processor_type) {
  39. if (processor_type == "x64") {
  40. finder_ = std::unique_ptr<Rel32Finder>(new Rel32FinderX64(
  41. relocs_start_rva_, relocs_end_rva_, image_end_rva_));
  42. } else if (processor_type == "x86") {
  43. finder_ = std::unique_ptr<Rel32Finder>(
  44. new Rel32FinderX86(relocs_start_rva_, relocs_end_rva_));
  45. } else {
  46. NOTREACHED();
  47. }
  48. }
  49. private:
  50. std::unique_ptr<Rel32Finder> finder_;
  51. RVA text_start_rva_;
  52. RVA text_end_rva_;
  53. RVA relocs_start_rva_;
  54. RVA relocs_end_rva_;
  55. RVA image_end_rva_;
  56. std::vector<uint8_t> text_data_;
  57. std::vector<RVA> abs32_locations_;
  58. std::vector<RVA> expected_rel32_locations_;
  59. // Scans |iss| for the next non-empty line, after removing "#"-style comments
  60. // and stripping trailing spaces. On success, returns true and writes the
  61. // result to |line_out|. Otherwise returns false.
  62. bool ReadNonEmptyLine(std::istringstream& iss, std::string* line_out) {
  63. std::string line;
  64. while (std::getline(iss, line)) {
  65. // Trim comments and trailing spaces.
  66. size_t end_pos = std::min(line.find("#"), line.length());
  67. while (end_pos > 0 && line[end_pos - 1] == ' ')
  68. --end_pos;
  69. line.resize(end_pos);
  70. if (!line.empty())
  71. break;
  72. }
  73. if (line.empty())
  74. return false;
  75. line_out->swap(line);
  76. return true;
  77. }
  78. // Scans |iss| for the next non-empty line, and reads (hex) uint32_t into |v|.
  79. // Returns true iff successful.
  80. bool ReadHexUInt32(std::istringstream& iss, uint32_t* v) {
  81. std::string line;
  82. if (!ReadNonEmptyLine(iss, &line))
  83. return false;
  84. return sscanf(line.c_str(), "%X", v) == 1;
  85. }
  86. // Initializes the test case by parsing the multi-line string |test_data|
  87. // to extract Rel32Finder parameters, and read expected values.
  88. void LoadTestFromString(const std::string& test_data) {
  89. // The first lines (ignoring empty ones) specify RVA bounds.
  90. std::istringstream iss(test_data);
  91. std::string processor_type;
  92. ASSERT_TRUE(ReadNonEmptyLine(iss, &processor_type));
  93. ASSERT_TRUE(ReadHexUInt32(iss, &text_start_rva_));
  94. ASSERT_TRUE(ReadHexUInt32(iss, &text_end_rva_));
  95. ASSERT_TRUE(ReadHexUInt32(iss, &relocs_start_rva_));
  96. ASSERT_TRUE(ReadHexUInt32(iss, &relocs_end_rva_));
  97. ASSERT_TRUE(ReadHexUInt32(iss, &image_end_rva_));
  98. std::string line;
  99. // The Program section specifies instruction bytes. We require lines to be
  100. // formatted in "DUMPBIN /DISASM" style, i.e.,
  101. // "00401003: E8 00 00 00 00 call 00401008"
  102. // ^ ^ ^ ^ ^ ^
  103. // We extract up to 6 bytes per line. The remaining are ignored.
  104. const int kBytesBegin = 12;
  105. const int kBytesEnd = 17;
  106. ReadNonEmptyLine(iss, &line);
  107. ASSERT_EQ("Program:", line);
  108. while (ReadNonEmptyLine(iss, &line) && line != "Abs32:") {
  109. std::string toks = line.substr(kBytesBegin, kBytesEnd);
  110. uint32_t vals[6];
  111. int num_read = sscanf(toks.c_str(), "%X %X %X %X %X %X", &vals[0],
  112. &vals[1], &vals[2], &vals[3], &vals[4], &vals[5]);
  113. for (int i = 0; i < num_read; ++i)
  114. text_data_.push_back(static_cast<uint8_t>(vals[i] & 0xFF));
  115. }
  116. ASSERT_FALSE(text_data_.empty());
  117. // The Abs32 section specifies hex RVAs, one per line.
  118. ASSERT_EQ("Abs32:", line);
  119. while (ReadNonEmptyLine(iss, &line) && line != "Expected:") {
  120. RVA abs32_location;
  121. ASSERT_EQ(1, sscanf(line.c_str(), "%X", &abs32_location));
  122. abs32_locations_.push_back(abs32_location);
  123. }
  124. // The Expected section specifies hex Rel32 RVAs, one per line.
  125. ASSERT_EQ("Expected:", line);
  126. while (ReadNonEmptyLine(iss, &line)) {
  127. RVA rel32_location;
  128. ASSERT_EQ(1, sscanf(line.c_str(), "%X", &rel32_location));
  129. expected_rel32_locations_.push_back(rel32_location);
  130. }
  131. CreateFinder(processor_type);
  132. }
  133. };
  134. class Rel32FinderTest : public BaseTest {
  135. public:
  136. void RunTest(const char* test_case_file) {
  137. Rel32FinderTestCase test_case(FileContents(test_case_file));
  138. test_case.RunTestBasic(test_case_file);
  139. }
  140. };
  141. TEST_F(Rel32FinderTest, TestBasic) {
  142. RunTest("rel32_x86_01.txt");
  143. RunTest("rel32_x86_02.txt");
  144. RunTest("rel32_x86_03.txt");
  145. RunTest("rel32_x86_04.txt");
  146. RunTest("rel32_x64_01.txt");
  147. RunTest("rel32_x64_02.txt");
  148. RunTest("rel32_x64_03.txt");
  149. }
  150. } // namespace
  151. } // namespace courgette