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.
5 #include "courgette/rel32_finder_win32_x86.h"
11 #include "base/macros.h"
12 #include "courgette/base_test_unittest.h"
13 #include "courgette/image_utils.h"
14 #include "testing/gtest/include/gtest/gtest.h"
20 // Helper class to load and execute a Rel32FinderWin32X86 test case.
21 class Rel32FinderWin32X86TestCase
{
23 Rel32FinderWin32X86TestCase(const std::string
& test_data
)
29 LoadTestFromString(test_data
);
32 void RunTestBasic(std::string name
) {
33 Rel32FinderWin32X86_Basic
finder(relocs_start_rva_
, relocs_end_rva_
,
35 ASSERT_FALSE(text_data_
.empty());
36 finder
.Find(&text_data_
[0], &text_data_
[0] + text_data_
.size(),
37 text_start_rva_
, text_end_rva_
, abs32_locations_
);
38 std::vector
<RVA
> rel32_locations
;
39 finder
.SwapRel32Locations(&rel32_locations
);
40 EXPECT_EQ(expected_rel32_locations_
, rel32_locations
)
41 << "From test case " << name
<< " (addresses are in hex)";
47 RVA relocs_start_rva_
;
50 std::vector
<uint8
> text_data_
;
51 std::vector
<RVA
> abs32_locations_
;
52 std::vector
<RVA
> expected_rel32_locations_
;
54 // Scans |iss| for the next non-empty line, after removing "#"-style comments
55 // and stripping trailing spaces. On success, returns true and writes the
56 // result to |line_out|. Otherwise returns false.
57 bool ReadNonEmptyLine(std::istringstream
& iss
, std::string
* line_out
) {
59 while (std::getline(iss
, line
)) {
60 // Trim comments and trailing spaces.
61 size_t end_pos
= std::min(line
.find("#"), line
.length());
62 while (end_pos
> 0 && line
[end_pos
] == ' ')
74 // Scans |iss| for the next non-empty line, and reads (hex) uint32 into |v|.
75 // Returns true iff successful.
76 bool ReadHexUInt32(std::istringstream
& iss
, uint32
* v
) {
78 if (!ReadNonEmptyLine(iss
, &line
))
80 return sscanf(line
.c_str(), "%X", v
) == 1;
83 // Initializes the test case by parsing the multi-line string |test_data|
84 // to extract Rel32FinderWin32X86 parameters, and read expected values.
85 void LoadTestFromString(const std::string
& test_data
) {
86 // The first lines (ignoring empty ones) specify RVA bounds.
87 std::istringstream
iss(test_data
);
88 ASSERT_TRUE(ReadHexUInt32(iss
, &text_start_rva_
));
89 ASSERT_TRUE(ReadHexUInt32(iss
, &text_end_rva_
));
90 ASSERT_TRUE(ReadHexUInt32(iss
, &relocs_start_rva_
));
91 ASSERT_TRUE(ReadHexUInt32(iss
, &relocs_end_rva_
));
92 ASSERT_TRUE(ReadHexUInt32(iss
, &image_end_rva_
));
95 // The Program section specifies instruction bytes. We require lines to be
96 // formatted in "DUMPBIN /DISASM" style, i.e.,
97 // "00401003: E8 00 00 00 00 call 00401008"
99 // We extract up to 6 bytes per line. The remaining are ignored.
100 const int kBytesBegin
= 12;
101 const int kBytesEnd
= 17;
102 ReadNonEmptyLine(iss
, &line
);
103 ASSERT_EQ("Program:", line
);
104 while (ReadNonEmptyLine(iss
, &line
) && line
!= "Abs32:") {
105 std::string toks
= line
.substr(kBytesBegin
, kBytesEnd
);
107 int num_read
= sscanf(toks
.c_str(), "%X %X %X %X %X %X", &vals
[0],
108 &vals
[1], &vals
[2], &vals
[3], &vals
[4], &vals
[5]);
109 for (int i
= 0; i
< num_read
; ++i
)
110 text_data_
.push_back(static_cast<uint8
>(vals
[i
] & 0xFF));
112 ASSERT_FALSE(text_data_
.empty());
114 // The Abs32 section specifies hex RVAs, one per line.
115 ASSERT_EQ("Abs32:", line
);
116 while (ReadNonEmptyLine(iss
, &line
) && line
!= "Expected:") {
118 ASSERT_EQ(1, sscanf(line
.c_str(), "%X", &abs32_location
));
119 abs32_locations_
.push_back(abs32_location
);
122 // The Expected section specifies hex Rel32 RVAs, one per line.
123 ASSERT_EQ("Expected:", line
);
124 while (ReadNonEmptyLine(iss
, &line
)) {
126 ASSERT_EQ(1, sscanf(line
.c_str(), "%X", &rel32_location
));
127 expected_rel32_locations_
.push_back(rel32_location
);
132 class Rel32FinderWin32X86Test
: public BaseTest
{
134 void RunTest(const char* test_case_file
) {
135 Rel32FinderWin32X86TestCase
test_case(FileContents(test_case_file
));
136 test_case
.RunTestBasic(test_case_file
);
140 TEST_F(Rel32FinderWin32X86Test
, TestBasic
) {
141 RunTest("rel32_win32_x86_01.txt");
142 RunTest("rel32_win32_x86_02.txt");
143 RunTest("rel32_win32_x86_03.txt");
144 RunTest("rel32_win32_x86_04.txt");
149 } // namespace courgette