michael@0: // Copyright (c) 2010, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: // Unit tests for FileID michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: michael@0: #include "common/linux/elfutils.h" michael@0: #include "common/linux/file_id.h" michael@0: #include "common/linux/safe_readlink.h" michael@0: #include "common/linux/synth_elf.h" michael@0: #include "common/test_assembler.h" michael@0: #include "common/tests/auto_tempdir.h" michael@0: #include "common/using_std_string.h" michael@0: #include "breakpad_googletest_includes.h" michael@0: michael@0: using namespace google_breakpad; michael@0: using google_breakpad::ElfClass32; michael@0: using google_breakpad::ElfClass64; michael@0: using google_breakpad::SafeReadLink; michael@0: using google_breakpad::synth_elf::ELF; michael@0: using google_breakpad::synth_elf::Notes; michael@0: using google_breakpad::test_assembler::kLittleEndian; michael@0: using google_breakpad::test_assembler::Section; michael@0: using ::testing::Types; michael@0: michael@0: namespace { michael@0: michael@0: // Simply calling Section::Append(size, byte) produces a uninteresting pattern michael@0: // that tends to get hashed to 0000...0000. This populates the section with michael@0: // data to produce better hashes. michael@0: void PopulateSection(Section* section, int size, int prime_number) { michael@0: for (int i = 0; i < size; i++) michael@0: section->Append(1, (i % prime_number) % 256); michael@0: } michael@0: michael@0: } // namespace michael@0: michael@0: TEST(FileIDStripTest, StripSelf) { michael@0: // Calculate the File ID of this binary using michael@0: // FileID::ElfFileIdentifier, then make a copy of this binary, michael@0: // strip it, and ensure that the result is the same. michael@0: char exe_name[PATH_MAX]; michael@0: ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name)); michael@0: michael@0: // copy our binary to a temp file, and strip it michael@0: AutoTempDir temp_dir; michael@0: string templ = temp_dir.path() + "/file-id-unittest"; michael@0: char cmdline[4096]; michael@0: sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ.c_str()); michael@0: ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline; michael@0: sprintf(cmdline, "chmod u+w \"%s\"", templ.c_str()); michael@0: ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline; michael@0: sprintf(cmdline, "strip \"%s\"", templ.c_str()); michael@0: ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline; michael@0: michael@0: uint8_t identifier1[sizeof(MDGUID)]; michael@0: uint8_t identifier2[sizeof(MDGUID)]; michael@0: FileID fileid1(exe_name); michael@0: EXPECT_TRUE(fileid1.ElfFileIdentifier(identifier1)); michael@0: FileID fileid2(templ.c_str()); michael@0: EXPECT_TRUE(fileid2.ElfFileIdentifier(identifier2)); michael@0: char identifier_string1[37]; michael@0: char identifier_string2[37]; michael@0: FileID::ConvertIdentifierToString(identifier1, identifier_string1, michael@0: 37); michael@0: FileID::ConvertIdentifierToString(identifier2, identifier_string2, michael@0: 37); michael@0: EXPECT_STREQ(identifier_string1, identifier_string2); michael@0: } michael@0: michael@0: template michael@0: class FileIDTest : public testing::Test { michael@0: public: michael@0: void GetElfContents(ELF& elf) { michael@0: string contents; michael@0: ASSERT_TRUE(elf.GetContents(&contents)); michael@0: ASSERT_LT(0U, contents.size()); michael@0: michael@0: elfdata_v.clear(); michael@0: elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end()); michael@0: elfdata = &elfdata_v[0]; michael@0: } michael@0: michael@0: vector elfdata_v; michael@0: uint8_t* elfdata; michael@0: }; michael@0: michael@0: typedef Types ElfClasses; michael@0: michael@0: TYPED_TEST_CASE(FileIDTest, ElfClasses); michael@0: michael@0: TYPED_TEST(FileIDTest, ElfClass) { michael@0: uint8_t identifier[sizeof(MDGUID)]; michael@0: const char expected_identifier_string[] = michael@0: "80808080-8080-0000-0000-008080808080"; michael@0: char identifier_string[sizeof(expected_identifier_string)]; michael@0: const size_t kTextSectionSize = 128; michael@0: michael@0: ELF elf(EM_386, TypeParam::kClass, kLittleEndian); michael@0: Section text(kLittleEndian); michael@0: for (size_t i = 0; i < kTextSectionSize; ++i) { michael@0: text.D8(i * 3); michael@0: } michael@0: elf.AddSection(".text", text, SHT_PROGBITS); michael@0: elf.Finish(); michael@0: this->GetElfContents(elf); michael@0: michael@0: EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, michael@0: identifier)); michael@0: michael@0: FileID::ConvertIdentifierToString(identifier, identifier_string, michael@0: sizeof(identifier_string)); michael@0: EXPECT_STREQ(expected_identifier_string, identifier_string); michael@0: } michael@0: michael@0: TYPED_TEST(FileIDTest, BuildID) { michael@0: const uint8_t kExpectedIdentifier[sizeof(MDGUID)] = michael@0: {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, michael@0: 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; michael@0: char expected_identifier_string[] = michael@0: "00000000-0000-0000-0000-000000000000"; michael@0: FileID::ConvertIdentifierToString(kExpectedIdentifier, michael@0: expected_identifier_string, michael@0: sizeof(expected_identifier_string)); michael@0: michael@0: uint8_t identifier[sizeof(MDGUID)]; michael@0: char identifier_string[sizeof(expected_identifier_string)]; michael@0: michael@0: ELF elf(EM_386, TypeParam::kClass, kLittleEndian); michael@0: Section text(kLittleEndian); michael@0: text.Append(4096, 0); michael@0: elf.AddSection(".text", text, SHT_PROGBITS); michael@0: Notes notes(kLittleEndian); michael@0: notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifier, michael@0: sizeof(kExpectedIdentifier)); michael@0: elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE); michael@0: elf.Finish(); michael@0: this->GetElfContents(elf); michael@0: michael@0: EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, michael@0: identifier)); michael@0: michael@0: FileID::ConvertIdentifierToString(identifier, identifier_string, michael@0: sizeof(identifier_string)); michael@0: EXPECT_STREQ(expected_identifier_string, identifier_string); michael@0: } michael@0: michael@0: TYPED_TEST(FileIDTest, BuildIDPH) { michael@0: const uint8_t kExpectedIdentifier[sizeof(MDGUID)] = michael@0: {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, michael@0: 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; michael@0: char expected_identifier_string[] = michael@0: "00000000-0000-0000-0000-000000000000"; michael@0: FileID::ConvertIdentifierToString(kExpectedIdentifier, michael@0: expected_identifier_string, michael@0: sizeof(expected_identifier_string)); michael@0: michael@0: uint8_t identifier[sizeof(MDGUID)]; michael@0: char identifier_string[sizeof(expected_identifier_string)]; michael@0: michael@0: ELF elf(EM_386, TypeParam::kClass, kLittleEndian); michael@0: Section text(kLittleEndian); michael@0: text.Append(4096, 0); michael@0: elf.AddSection(".text", text, SHT_PROGBITS); michael@0: Notes notes(kLittleEndian); michael@0: notes.AddNote(0, "Linux", michael@0: reinterpret_cast("\0x42\0x02\0\0"), 4); michael@0: notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifier, michael@0: sizeof(kExpectedIdentifier)); michael@0: int note_idx = elf.AddSection(".note", notes, SHT_NOTE); michael@0: elf.AddSegment(note_idx, note_idx, PT_NOTE); michael@0: elf.Finish(); michael@0: this->GetElfContents(elf); michael@0: michael@0: EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, michael@0: identifier)); michael@0: michael@0: FileID::ConvertIdentifierToString(identifier, identifier_string, michael@0: sizeof(identifier_string)); michael@0: EXPECT_STREQ(expected_identifier_string, identifier_string); michael@0: } michael@0: michael@0: // Test to make sure two files with different text sections produce michael@0: // different hashes when not using a build id. michael@0: TYPED_TEST(FileIDTest, UniqueHashes) { michael@0: char identifier_string_1[] = michael@0: "00000000-0000-0000-0000-000000000000"; michael@0: char identifier_string_2[] = michael@0: "00000000-0000-0000-0000-000000000000"; michael@0: uint8_t identifier_1[sizeof(MDGUID)]; michael@0: uint8_t identifier_2[sizeof(MDGUID)]; michael@0: michael@0: { michael@0: ELF elf1(EM_386, TypeParam::kClass, kLittleEndian); michael@0: Section foo_1(kLittleEndian); michael@0: PopulateSection(&foo_1, 32, 5); michael@0: elf1.AddSection(".foo", foo_1, SHT_PROGBITS); michael@0: Section text_1(kLittleEndian); michael@0: PopulateSection(&text_1, 4096, 17); michael@0: elf1.AddSection(".text", text_1, SHT_PROGBITS); michael@0: elf1.Finish(); michael@0: this->GetElfContents(elf1); michael@0: } michael@0: michael@0: EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, michael@0: identifier_1)); michael@0: FileID::ConvertIdentifierToString(identifier_1, identifier_string_1, michael@0: sizeof(identifier_string_1)); michael@0: michael@0: { michael@0: ELF elf2(EM_386, TypeParam::kClass, kLittleEndian); michael@0: Section text_2(kLittleEndian); michael@0: Section foo_2(kLittleEndian); michael@0: PopulateSection(&foo_2, 32, 5); michael@0: elf2.AddSection(".foo", foo_2, SHT_PROGBITS); michael@0: PopulateSection(&text_2, 4096, 31); michael@0: elf2.AddSection(".text", text_2, SHT_PROGBITS); michael@0: elf2.Finish(); michael@0: this->GetElfContents(elf2); michael@0: } michael@0: michael@0: EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, michael@0: identifier_2)); michael@0: FileID::ConvertIdentifierToString(identifier_2, identifier_string_2, michael@0: sizeof(identifier_string_2)); michael@0: michael@0: EXPECT_STRNE(identifier_string_1, identifier_string_2); michael@0: }