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