|
1 // Copyright (c) 2010, Google Inc. |
|
2 // All rights reserved. |
|
3 // |
|
4 // Redistribution and use in source and binary forms, with or without |
|
5 // modification, are permitted provided that the following conditions are |
|
6 // met: |
|
7 // |
|
8 // * Redistributions of source code must retain the above copyright |
|
9 // notice, this list of conditions and the following disclaimer. |
|
10 // * Redistributions in binary form must reproduce the above |
|
11 // copyright notice, this list of conditions and the following disclaimer |
|
12 // in the documentation and/or other materials provided with the |
|
13 // distribution. |
|
14 // * Neither the name of Google Inc. nor the names of its |
|
15 // contributors may be used to endorse or promote products derived from |
|
16 // this software without specific prior written permission. |
|
17 // |
|
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
29 |
|
30 // Unit tests for FileID |
|
31 |
|
32 #include <elf.h> |
|
33 #include <stdlib.h> |
|
34 |
|
35 #include <string> |
|
36 |
|
37 #include "common/linux/elfutils.h" |
|
38 #include "common/linux/file_id.h" |
|
39 #include "common/linux/safe_readlink.h" |
|
40 #include "common/linux/synth_elf.h" |
|
41 #include "common/test_assembler.h" |
|
42 #include "common/tests/auto_tempdir.h" |
|
43 #include "common/using_std_string.h" |
|
44 #include "breakpad_googletest_includes.h" |
|
45 |
|
46 using namespace google_breakpad; |
|
47 using google_breakpad::ElfClass32; |
|
48 using google_breakpad::ElfClass64; |
|
49 using google_breakpad::SafeReadLink; |
|
50 using google_breakpad::synth_elf::ELF; |
|
51 using google_breakpad::synth_elf::Notes; |
|
52 using google_breakpad::test_assembler::kLittleEndian; |
|
53 using google_breakpad::test_assembler::Section; |
|
54 using ::testing::Types; |
|
55 |
|
56 namespace { |
|
57 |
|
58 // Simply calling Section::Append(size, byte) produces a uninteresting pattern |
|
59 // that tends to get hashed to 0000...0000. This populates the section with |
|
60 // data to produce better hashes. |
|
61 void PopulateSection(Section* section, int size, int prime_number) { |
|
62 for (int i = 0; i < size; i++) |
|
63 section->Append(1, (i % prime_number) % 256); |
|
64 } |
|
65 |
|
66 } // namespace |
|
67 |
|
68 TEST(FileIDStripTest, StripSelf) { |
|
69 // Calculate the File ID of this binary using |
|
70 // FileID::ElfFileIdentifier, then make a copy of this binary, |
|
71 // strip it, and ensure that the result is the same. |
|
72 char exe_name[PATH_MAX]; |
|
73 ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name)); |
|
74 |
|
75 // copy our binary to a temp file, and strip it |
|
76 AutoTempDir temp_dir; |
|
77 string templ = temp_dir.path() + "/file-id-unittest"; |
|
78 char cmdline[4096]; |
|
79 sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ.c_str()); |
|
80 ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline; |
|
81 sprintf(cmdline, "chmod u+w \"%s\"", templ.c_str()); |
|
82 ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline; |
|
83 sprintf(cmdline, "strip \"%s\"", templ.c_str()); |
|
84 ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline; |
|
85 |
|
86 uint8_t identifier1[sizeof(MDGUID)]; |
|
87 uint8_t identifier2[sizeof(MDGUID)]; |
|
88 FileID fileid1(exe_name); |
|
89 EXPECT_TRUE(fileid1.ElfFileIdentifier(identifier1)); |
|
90 FileID fileid2(templ.c_str()); |
|
91 EXPECT_TRUE(fileid2.ElfFileIdentifier(identifier2)); |
|
92 char identifier_string1[37]; |
|
93 char identifier_string2[37]; |
|
94 FileID::ConvertIdentifierToString(identifier1, identifier_string1, |
|
95 37); |
|
96 FileID::ConvertIdentifierToString(identifier2, identifier_string2, |
|
97 37); |
|
98 EXPECT_STREQ(identifier_string1, identifier_string2); |
|
99 } |
|
100 |
|
101 template<typename ElfClass> |
|
102 class FileIDTest : public testing::Test { |
|
103 public: |
|
104 void GetElfContents(ELF& elf) { |
|
105 string contents; |
|
106 ASSERT_TRUE(elf.GetContents(&contents)); |
|
107 ASSERT_LT(0U, contents.size()); |
|
108 |
|
109 elfdata_v.clear(); |
|
110 elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end()); |
|
111 elfdata = &elfdata_v[0]; |
|
112 } |
|
113 |
|
114 vector<uint8_t> elfdata_v; |
|
115 uint8_t* elfdata; |
|
116 }; |
|
117 |
|
118 typedef Types<ElfClass32, ElfClass64> ElfClasses; |
|
119 |
|
120 TYPED_TEST_CASE(FileIDTest, ElfClasses); |
|
121 |
|
122 TYPED_TEST(FileIDTest, ElfClass) { |
|
123 uint8_t identifier[sizeof(MDGUID)]; |
|
124 const char expected_identifier_string[] = |
|
125 "80808080-8080-0000-0000-008080808080"; |
|
126 char identifier_string[sizeof(expected_identifier_string)]; |
|
127 const size_t kTextSectionSize = 128; |
|
128 |
|
129 ELF elf(EM_386, TypeParam::kClass, kLittleEndian); |
|
130 Section text(kLittleEndian); |
|
131 for (size_t i = 0; i < kTextSectionSize; ++i) { |
|
132 text.D8(i * 3); |
|
133 } |
|
134 elf.AddSection(".text", text, SHT_PROGBITS); |
|
135 elf.Finish(); |
|
136 this->GetElfContents(elf); |
|
137 |
|
138 EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, |
|
139 identifier)); |
|
140 |
|
141 FileID::ConvertIdentifierToString(identifier, identifier_string, |
|
142 sizeof(identifier_string)); |
|
143 EXPECT_STREQ(expected_identifier_string, identifier_string); |
|
144 } |
|
145 |
|
146 TYPED_TEST(FileIDTest, BuildID) { |
|
147 const uint8_t kExpectedIdentifier[sizeof(MDGUID)] = |
|
148 {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
|
149 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; |
|
150 char expected_identifier_string[] = |
|
151 "00000000-0000-0000-0000-000000000000"; |
|
152 FileID::ConvertIdentifierToString(kExpectedIdentifier, |
|
153 expected_identifier_string, |
|
154 sizeof(expected_identifier_string)); |
|
155 |
|
156 uint8_t identifier[sizeof(MDGUID)]; |
|
157 char identifier_string[sizeof(expected_identifier_string)]; |
|
158 |
|
159 ELF elf(EM_386, TypeParam::kClass, kLittleEndian); |
|
160 Section text(kLittleEndian); |
|
161 text.Append(4096, 0); |
|
162 elf.AddSection(".text", text, SHT_PROGBITS); |
|
163 Notes notes(kLittleEndian); |
|
164 notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifier, |
|
165 sizeof(kExpectedIdentifier)); |
|
166 elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE); |
|
167 elf.Finish(); |
|
168 this->GetElfContents(elf); |
|
169 |
|
170 EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, |
|
171 identifier)); |
|
172 |
|
173 FileID::ConvertIdentifierToString(identifier, identifier_string, |
|
174 sizeof(identifier_string)); |
|
175 EXPECT_STREQ(expected_identifier_string, identifier_string); |
|
176 } |
|
177 |
|
178 TYPED_TEST(FileIDTest, BuildIDPH) { |
|
179 const uint8_t kExpectedIdentifier[sizeof(MDGUID)] = |
|
180 {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
|
181 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; |
|
182 char expected_identifier_string[] = |
|
183 "00000000-0000-0000-0000-000000000000"; |
|
184 FileID::ConvertIdentifierToString(kExpectedIdentifier, |
|
185 expected_identifier_string, |
|
186 sizeof(expected_identifier_string)); |
|
187 |
|
188 uint8_t identifier[sizeof(MDGUID)]; |
|
189 char identifier_string[sizeof(expected_identifier_string)]; |
|
190 |
|
191 ELF elf(EM_386, TypeParam::kClass, kLittleEndian); |
|
192 Section text(kLittleEndian); |
|
193 text.Append(4096, 0); |
|
194 elf.AddSection(".text", text, SHT_PROGBITS); |
|
195 Notes notes(kLittleEndian); |
|
196 notes.AddNote(0, "Linux", |
|
197 reinterpret_cast<const uint8_t *>("\0x42\0x02\0\0"), 4); |
|
198 notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifier, |
|
199 sizeof(kExpectedIdentifier)); |
|
200 int note_idx = elf.AddSection(".note", notes, SHT_NOTE); |
|
201 elf.AddSegment(note_idx, note_idx, PT_NOTE); |
|
202 elf.Finish(); |
|
203 this->GetElfContents(elf); |
|
204 |
|
205 EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, |
|
206 identifier)); |
|
207 |
|
208 FileID::ConvertIdentifierToString(identifier, identifier_string, |
|
209 sizeof(identifier_string)); |
|
210 EXPECT_STREQ(expected_identifier_string, identifier_string); |
|
211 } |
|
212 |
|
213 // Test to make sure two files with different text sections produce |
|
214 // different hashes when not using a build id. |
|
215 TYPED_TEST(FileIDTest, UniqueHashes) { |
|
216 char identifier_string_1[] = |
|
217 "00000000-0000-0000-0000-000000000000"; |
|
218 char identifier_string_2[] = |
|
219 "00000000-0000-0000-0000-000000000000"; |
|
220 uint8_t identifier_1[sizeof(MDGUID)]; |
|
221 uint8_t identifier_2[sizeof(MDGUID)]; |
|
222 |
|
223 { |
|
224 ELF elf1(EM_386, TypeParam::kClass, kLittleEndian); |
|
225 Section foo_1(kLittleEndian); |
|
226 PopulateSection(&foo_1, 32, 5); |
|
227 elf1.AddSection(".foo", foo_1, SHT_PROGBITS); |
|
228 Section text_1(kLittleEndian); |
|
229 PopulateSection(&text_1, 4096, 17); |
|
230 elf1.AddSection(".text", text_1, SHT_PROGBITS); |
|
231 elf1.Finish(); |
|
232 this->GetElfContents(elf1); |
|
233 } |
|
234 |
|
235 EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, |
|
236 identifier_1)); |
|
237 FileID::ConvertIdentifierToString(identifier_1, identifier_string_1, |
|
238 sizeof(identifier_string_1)); |
|
239 |
|
240 { |
|
241 ELF elf2(EM_386, TypeParam::kClass, kLittleEndian); |
|
242 Section text_2(kLittleEndian); |
|
243 Section foo_2(kLittleEndian); |
|
244 PopulateSection(&foo_2, 32, 5); |
|
245 elf2.AddSection(".foo", foo_2, SHT_PROGBITS); |
|
246 PopulateSection(&text_2, 4096, 31); |
|
247 elf2.AddSection(".text", text_2, SHT_PROGBITS); |
|
248 elf2.Finish(); |
|
249 this->GetElfContents(elf2); |
|
250 } |
|
251 |
|
252 EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, |
|
253 identifier_2)); |
|
254 FileID::ConvertIdentifierToString(identifier_2, identifier_string_2, |
|
255 sizeof(identifier_string_2)); |
|
256 |
|
257 EXPECT_STRNE(identifier_string_1, identifier_string_2); |
|
258 } |